Merge two different tables with Access - sql

I’d would like to merge two different tables with similar and different columns. The only different columns are : Amount-F21 and Amount A-21. My issue is when I write the SQL request (UNION ALL) with Access, it deletes the column Amount A-21 but I need this one though. Thanks.
SELECT * FROM [Source Alloc-A21]
UNION ALL
SELECT * FROM [Source Alloc-F21]

To use the star notation Table.* with UNION, the columns in both tables must be equal. If they are not, you need to select individual columns and provide default values for the columns that are missing for both tables.
For example:
SELECT TableA.A, TableA.B, TableA.[Amount-F21], 0 AS [Amount-A21]
FROM TableA
UNION ALL
SELECT TableB.A, TableB.B, 0 AS [Amount-F21], TableB.[Amount-A21]
FROM TableB
This will report 0 for any of the missing columns (Amount-F21 or Amount A-21).
You can then sum the results to hide the zero (default) values.
SELECT T.A, T.B, SUM(T.[Amount-F21]) AS [Amount-F21], SUM(T.[Amount-A21]) AS [Amount-A21]
FROM (
SELECT TableA.A, TableA.B, TableA.[Amount-F21], 0 AS [Amount-A21]
FROM TableA
UNION ALL
SELECT TableB.A, TableB.B, 0 AS [Amount-F21], TableB.[Amount-A21]
FROM TableB
) AS T
GROUP BY T.A, T.B

Related

Query to return nonmatching lines in two arbitrary tables

I have two sets of tables (i.e. a.1, a.2, a.3, b.1, b.2, b.3, etc) created using slightly different logic. The analogous table in the two schemas have the exact same columns (i.e. a.1 has the same columns as b.1). My belief is that the tables in the two schemas should contain the exact same information, but I want to test that belief. Therefore I want to write a query that compares two analogous tables and returns lines that are not in both tables. Is there an easy way to write a query to do that without manually writing the join? In other words, can I have a query that can produce the results that I want where I only have to change the table names I want to compare while leaving the rest of the query unchanged?
To be a bit more explicit, I'm looking to do something like the following:
select *
from a.1
where (all columns in a.1) not in (select * from b.1);
If I could write something like this then all I would have to do to compare a.2 to b.2 would be to change the table names. However, it's not clear to me how to come up with the (all columns in a.1) piece in a general way.
Based on a recommendation in the comments, I've created the following showing the kind of thing I'd like to see:
https://dbfiddle.uk/?rdbms=db2_11.1&fiddle=ad0141b0daf8f8f92e6e3fa8d57e67ad
I was looking for the except clause.
So
select *
from a.1
where (all columns in a.1) not in (select * from b.1);
can be written as
select * from a.1
except
select * from b.1
In db-fiddle I give an explicit exmaple of what I wanted.
If you have a primary key to match rows between the tables, then you can try a full anti-join. For example:
select a.id as aid, b.id as bid
from a
full join b on b.id = a.id
where a.id is null or b.id is null
If the tables are:
A: 1, 2, 3
B: 1, 2, 4
The result is:
AID BID
---- ----
null 4 -- means it's present in B, but not in A
3 null -- means it's present in A, but not in B
See running example at db<>fiddle.
Of course, if your tables do not have a primary key, or if the rows are inconsistent (same PK, different data), then you'll need to adjust the query.
As an alternative you can try this:
select 'a1' t,* from (
select a1.*,row_number() over (partition by c1 order by 1) as rn from a1
minus
select b1.*,row_number() over (partition by c1 order by 1) as rn from b1
)
union all
select 'b1' t,* from (
select b1.*,row_number() over (partition by c1 order by 1) as rn from b1
minus
select a1.*,row_number() over (partition by c1 order by 1) as rn from a1
)
fiddle
edit: you can shorten the query by precalculating the rn part, instead of doing the same calculation again.

Efficient way to verify a table is a subset of another table

I have two tables A and B, the structures are exactly the same. I need to verify A is a subset of B. Because the structure contains over 100 fields, I do not want to list them one by one in a where predicates.
I would like to know if there is any more easier way to do that
Assumptions:
(1) Identical table structure of A and B. This means that both order of columns and their data types have to match.
(2) There are no duplicate rows in table A
Problem description
To prove that A is a subset of B you need to show that A\B = empty set.
Solution
This means that if you remove every row in A that has a matching row in B and your output is empty (0 rows) this means that A is subset of B.
If on the other hand, in the output you get > 0 rows it means that A has rows that B doesnt and that A isn't a subset of B.
SELECT * FROM A
EXCEPT
SELECT * FROM B
When A is empty (contains 0 rows) it will be treated as a subset of B, because the result of above query will be 0 rows.
#robertoplancarte's approach with little tweaking
with tB_cnt as
(
SELECT COUNT(*) cnt FROM
(
SELECT DISTINCT * FROM dbo.T_B
) T_B
), TAB_cnt as
(
SELECT count(*) cnt FROM
(
SELECT * FROM dto.T_B
UNION
SELECT * FROM dto.T_A
) T_AB
)
SELECT
CASE WHEN TB_CNT.CNT = TAB_CNT.CNT THEN
'Table A is subset of B'
else
'Table A is not subset of B'
END as Result
FROM TAB_CNT, TB_CNT

Order by data as per supplied Id in sql

Query:
SELECT *
FROM [MemberBackup].[dbo].[OriginalBackup]
where ration_card_id in
(
1247881,174772,
808454,2326154
)
Right now the data is ordered by the auto id or whatever clause I'm passing in order by.
But I want the data to come in sequential format as per id's I have passed
Expected Output:
All Data for 1247881
All Data for 174772
All Data for 808454
All Data for 2326154
Note:
Number of Id's to be passed will 300 000
One option would be to create a CTE containing the ration_card_id values and the orders which you are imposing, and the join to this table:
WITH cte AS (
SELECT 1247881 AS ration_card_id, 1 AS position
UNION ALL
SELECT 174772, 2
UNION ALL
SELECT 808454, 3
UNION ALL
SELECT 2326154, 4
)
SELECT t1.*
FROM [MemberBackup].[dbo].[OriginalBackup] t1
INNER JOIN cte t2
ON t1.ration_card_id = t2.ration_card_id
ORDER BY t2.position DESC
Edit:
If you have many IDs, then neither the answer above nor the answer given using a CASE expression will suffice. In this case, your best bet would be to load the list of IDs into a table, containing an auto increment ID column. Then, each number would be labelled with a position as its record is being loaded into your database. After this, you can join as I have done above.
If the desired order does not reflect a sequential ordering of some preexisting data, you will have to specify the ordering yourself. One way to do this is with a case statement:
SELECT *
FROM [MemberBackup].[dbo].[OriginalBackup]
where ration_card_id in
(
1247881,174772,
808454,2326154
)
ORDER BY CASE ration_card_id
WHEN 1247881 THEN 0
WHEN 174772 THEN 1
WHEN 808454 THEN 2
WHEN 2326154 THEN 3
END
Stating the obvious but note that this ordering most likely is not represented by any indexes, and will therefore not be indexed.
Insert your ration_card_id's in #temp table with one identity column.
Re-write your sql query as:
SELECT a.*
FROM [MemberBackup].[dbo].[OriginalBackup] a
JOIN #temps b
on a.ration_card_id = b.ration_card_id
order by b.id

Compare sums of two columns in sql

I have two tables with a column named no_id, I want to test that the values in both columns on the tables are the same.
So I´m trying to sum the values of both columns and compare the result.
SELECT
CASE
WHEN SUM (cast(a.no_id as bigint)) = SUM(cast(b.no_id as bigint)) THEN 'YES'
ELSE 'NO'
END as no_id
FROM table_a as a
,table_b as b
The result of the query is NO, but when I select each sum:
SELECT
SUM (cast(a.no_id as bigint)),
SUM(cast(b.no_id as bigint))
FROM table_a as a
,table_b as b
I got two nulls, one in each column. Instead of the sums of the columns.
I have to do this with other twenty columns of both tables.
no_id is a varchar(16) in both tables.
------UPDATE------
no_id only contains numeric strings,
I did the next query to ensure that null would be treated as 0:
SELECT
SUM(cast(ISNULL(a.no_id,0) as bigint)),
SUM(cast(ISNULL(b.no_id,0) as bigint))
FROM table_a as a
,table_b as b
But I keep getting the same result.
If I select the result from just one table, it works, I get the result of the sum:
SELECT
SUM(cast(ISNULL(a.no_id,0) as bigint))
FROM table_a as a
Then, why it doesn't work with both tables?
As I said in the comments:
Don't use the comma(,) operator in the FROM clause, it's been obsolete for over twenty years, use the JOIN keyword syntax instead. Because if you did you would know that ...
You are CROSS JOIN-ing your tables which is very bad and logically wrong 99% of the time. You need to use column subqueries instead.
Like this:
SELECT
CASE
WHEN (SELECT SUM(cast(no_id as bigint)) FROM table_a)
= (SELECT SUM(cast(no_id as bigint)) FROM table_b) THEN 'YES'
ELSE 'NO'
END as no_id
The problem is you don't say how table_a and table_b are related, so every row in table_a is paired with every row in table_b if either table has even one null value, then the total sum will be null.
If you just want the SUMS in both tables to match, then I;m not sure SUM is the best indicator. If table A had the values 1 and 4, and table b had 2 and 3, then the SUM would match but obviously the values are different.

differentiate rows in a union table

I m selecting data from two different tables with no matching columns using this sql query
select * from (SELECT s.shout_id, s.user_id, s.time FROM shouts s
union all
select v.post_id, v.sender_user_id, v.time from void_post v)
as derived_table order by time desc;
Now is there any other way or with this sql statement only can i
differentiate the data from the two tables.
I was thinking of a dummy row that can be created at run-time(in the select statement only ) which would flag the row from the either tables.
As there is no way i can differentiate the shout_id that is thrown in the unioned table is
shout_id from the shout table or from the void_post table.
Thanks
Pradyut
You can just include an extra column in each select (I'd suggest a BIT)
select * from
(SELECT s.shout_id, s.user_id, s.time, 1 AS FromShouts FROM shouts s
union all
select v.post_id, v.sender_user_id, v.time, 0 AS FromShouts from void_post v)
as derived_table order by time desc;
Sure, just add a new field in your select statement called something like source with a different constant value for each source.
SELECT s.shout_id, s.user_id, s.time, 'shouts' as source FROM shouts s
UNION ALL
SELECT v.post_id, v.sender_user_id, v.time, 'void_post' as source FROM void_post v
A dummy variable is a nice way to do it. There isn't much overhead in the grand scheme of things.
p.s., the dummy variable represents a column and not a row.