select query joining two tables on a range - sql

I have two tables:
Table A with columns
name | tag | price | ref
and Table B with columns:
id | time | min_ref | max_ref
I want to make the following query, take all columns from table A and columns id and time from Table B, combining rows in such a way that particular row from A is merged with a row from B if value ref from A is in the range (min_ref, max_ref). Example:
A
name | tag | price | ref
A | aaa | 78 | 456
B | bbb | 19 | 123
C | ccc | 5 | 789
B
id | time | min_ref | max_ref
0 | 26-01-2019 | 100 | 150
1 | 27-01-2019 | 450 | 525
2 | 25-01-2019 | 785 | 800
the query should return:
name | tag | price | ref | id | time
A | aaa | 78 | 456 | 1 | 27-01-2019
B | bbb | 19 | 123 | 0 | 26-01-2019
C | ccc | 5 | 789 | 2 | 25-01-2019

The notation (min_ref, max_ref) for ranges signifies exclusive bounds. Would be [min_ref, max_ref] for inclusive.
So:
select a.*, b.id, b.time
from a
join b on a.ref > b.min_ref
and a.ref < b.max_ref;
The BETWEEN predicate treats all bounds as inclusive.

I think this is just a join:
select a.*, b.id, b.time
from a join
b
on a.ref between b.min_ref and b.max_ref;

You want a JOIN which combines rows from the two tables with an appropriate criteria. For instance:
SELECT a.name, a.tag, a.price, a.ref, b.id, bi.time
FROM a
INNER JOIN b ON b.min_ref <= a.ref AND b.max_ref >= a.ref
The INNER JOIN finds matching rows from the two tables, ON a specified criteria. In this case, the criteria is that a.ref is between b.min_ref and b.max_ref.
You can also use the sql BETWEEN operator to simplify the conditionals:
SELECT ...
FROM a
INNER JOIN b ON a.ref BETWEEN b.min_ref AND b.max_ref

Related

SQL select all rows that are not equal to an id, and replace the id column with the value - without cross join

Say I have a table like this:
+----+-------+
| id | value |
+----+-------+
| 1 | a |
| 1 | b |
| 2 | c |
| 2 | d |
| 3 | e |
| 3 | f |
+----+-------+
And I want to select all rows with id that are not a, and change their id to a; select all rows with id that are not b, and change the id to b; and select all rows with id that are not c, and change their id to c.
Here is the output I want:
+----+-------+
| id | value |
+----+-------+
| 1 | c |
| 1 | d |
| 1 | e |
| 1 | f |
| 2 | a |
| 2 | b |
| 2 | e |
| 2 | f |
| 3 | a |
| 3 | b |
| 3 | c |
| 3 | d |
+----+-------+
The only solution I can think of is through cross join and distinct:
select distinct a.id, b.value
from table a
cross join table b
where a.id != b.id
Is there any other way to avoid such expensive operation?
I think the typical way to write this is to generate all pairs of id and value and then remove the ones that exist:
select i.id, v.value
from (select distinct id from t) i cross join
(select distinct value from t) v left join
t
on t.id = i.id and t.value = i.value
where t.id is null;
First, I don't think this is what your query does. But this is what you seem to be describing.
From a performance perspective, you might have other sources for i and v that don't require subqueries. If so, use those for performance.
Finally, I don't think you can do much to improve the performance of this, apart from using explicit tables -- and perhaps having appropriate indexes on all the tables.

Join three tables based on one key, putting data into same column

I have three tables that I am trying to join together to check that the proper data matches. I have table A which is a list of all accounts that a commission was paid on and what that commission amount was. I have Table B and Table C which are two tables that have commission calculations in it. The goal is to compare Table A to Table and to Table C and pulling back the amounts from both tables to ensure a match. The part I am struggling with is, Table A has all the accounts that are the base population. Table B has some and Table C as some. An account will be in either Table B or C, but never in both. I want to pull the payment from Table A, and then verify to the payment in Table B or C(whichever it occurs) and the same with commission. I then am doing a case when that compares the two fields and tells me if it matches are not.
+---------+---------+-----+------+
| Table A | | | |
+---------+---------+-----+------+
| Account | Uniq_ID | Pay | Comm |
| 12345 | ABCD | 100 | 10 |
| 23456 | OLPOL | 25 | 2 |
| 45678 | LKJHG | 200 | 15 |
| 96385 | LKJ67 | 250 | 26 |
+---------+---------+-----+------+
+---------+---------+-----+------+
| Table B | | | |
+---------+---------+-----+------+
| Account | Uniq_ID | Pay | Comm |
| 12345 | ABCD | 100 | 8 |
| 45678 | LKJHG | 200 | 15 |
+---------+---------+-----+------+
+---------+---------+-----+------+
| Table C | | | |
+---------+---------+-----+------+
| Account | Uniq_ID | Pay | Comm |
| 23456 | OLPOL | 25 | 2 |
| 96385 | LKJ67 | 250 | 32 |
+---------+---------+-----+------+
I am trying to get my results to show up in a columns called pay_ver and comm_verf, and it would populate with the data from either Table B or C based on which it matched with. I am hoping to have to output look like so....
+---------+---------+-----+----------+------+-----------+---------+
| Output | | | | | | |
+---------+---------+-----+----------+------+-----------+---------+
| Account | Uniq_ID | Pay | Pay_verf | comm | comm_Verf | Matched |
| 12345 | ABCD | 100 | 100 | 10 | 8 | No |
| 23456 | OLPOL | 25 | 25 | 2 | 2 | Yes |
| 45678 | LKJHG | 200 | 200 | 15 | 15 | Yes |
| 96385 | LKJ67 | 250 | 250 | 26 | 32 | No |
+---------+---------+-----+----------+------+-----------+---------+
This is the code I have used to join Table A to B, and Table A to C but I have done this in two separate queries giving me two outputs. I would like to be able to do this in one, so I only have one output.
select a.account, a.uniq_id, a.pay, b.pay as pay_verf, a.comm, b.comm as comm_verf,
CASE WHEN a.comm = b.comm THEN 'MATCHED'
ELSE 'UNMATCHED'
END as Matched
from tblA a
left join tblB b
on a.account = b.account
and a.uniq_id = b.uniq_id;
I can not just figure out how to also get it to join to Table C without adding an extra column.
You can do:
select
account, uniq_id, pay,
pay_total as pay_verf,
comm,
comm - comm_total as comm_verf,
case when comm = comm_total then 'Yes' else 'No' end as matched
from (
select
a.account, a.uniq_id, a.pay, a.comm,
coalesce(b.pay, 0) + coalesce(c.pay, 0) as pay_total,
coalesce(b.comm, 0) + coalesce(c.comm, 0) as comm_total
from table_a a
left join table_b b on a.account = b.account
left join table_c c on a.account = c.account
) x
You are very close. Just need to add one more join and an addition WHEN to your case statement. This should act like an if elseif else logic. So it checks if a.comm = b.comm and then checks a.comm = c.comm. If neither match if will set to unmatched. This works well because you stated the ID can't be in both B and C.
select a.account, a.uniq_id, a.pay, b.pay as pay_verf, a.comm, b.comm as comm_verf,
CASE WHEN a.comm = b.comm THEN 'MATCHED'
WHEN a.comm = c.comm THEN 'MATCHED'
ELSE 'UNMATCHED'
END as Matched
from tblA a
left join tblB b
on a.account = b.account
and a.uniq_id = b.uniq_id;
left join tblB c
on a.account = c.account
and a.uniq_id = c.uniq_id;
Yet another option could be something like
SELECT a.account, a.uniq_id, a.pay, bc.pay as pay_verf, a.comm, bc.comm as comm_verf
FROM a left join (
SELECT * from b
UNION ALL
SELECT * from c
) bc on (a.account = bc.account and a.uniq_id = bc.uniq_id)

SQL JOIN two table & show all rows for table A

I have a question about JOIN.
TABLE A | TABLE B |
-----------------------------------------|
PK | div | PK | div | val |
-----------------------------------------|
A | a | 1 | a | 10 |
B | b | 2 | a | 100 |
C | c | 3 | c | 9 |
------------------| 4 | c | 99 |
-----------------------
There are two tables something like above, and I have been trying to join two tables but I want to see all rows from TABLE A.
Something like
SELECT T1.PK, T1.div, T2.val
FROM A T1
LEFT OUTER JOIN B T2
ON T1.div = T2.div
and I want the result would look like this below.
PK | div | val |
-------------------------
A | a | 10 |
A | a | 100 |
B | null | null |
C | c | 9 |
C | c | 99 |
I have tried all JOINs I know but B doesn't appear because it doesn't exist. Is it possible to show all rows on TABLE A and just show null if it doesn't exists on TABLE B?
Thanks in advance!
If you change your query to
SELECT T1.PK, T2.div, T2.val
FROM A T1
LEFT OUTER JOIN B T2
ON T1.div = T2.div
(Note, that div comes from T2 here.), you'll get exactly the result posted (but maybe in a different order, add an ORDER BY clause if you want a specific order).
Your query as it stands will get you:
PK | div | val |
-------------------------
A | a | 10 |
A | a | 100 |
B | b | null |
C | c | 9 |
C | c | 99 |
(Note, that div is b for the row with the PK of B, not null.)
To get to your resultset, all you need to do is use T2.Div as that is the value that does not exist in the second table:
SELECT T1.PK, T2.div, T2.val
FROM A T1
LEFT OUTER JOIN B T2
ON T1.div = T2.div

Access Queries comparing two tables

I have two tables in Access, Table A and Table B:
Table MasterLockInsNew:
+----+-------+----------+
| ID | Value | Date |
+----+-------+----------+
| 1 | 123 | 12/02/13 |
| 2 | 1231 | 11/02/13 |
| 4 | 1265 | 16/02/13 |
+----+-------+----------+
Table InitialPolData:
+----+-------+----------+---+
| ID | Value | Date |Type
+----+-------+----------+---+
| 1 | 123 | 12/02/13 | x |
| 2 | 1231 | 11/02/13 | x |
| 3 | 1238 | 10/02/13 | y |
| 4 | 1265 | 16/02/13 | a |
| 7 | 7649 | 18/02/13 | z |
+----+-------+----------+---+
All I want are the rows from table B for IDs not contained in A. My current code looks like this:
SELECT Distinct InitialPolData.*
FROM InitialPolData
WHERE InitialPolData.ID NOT IN (SELECT Distinct InitialPolData.ID
from InitialPolData INNER JOIN
MasterLockInsNew
ON InitialPolData.ID=MasterLockInsNew.ID);
But whenever I run this in Access it crashes!! The tables are fairly large but I don't think this is the reason.
Can anyone help?
Thanks
or try a left outer join:
SELECT b.*
FROM InitialPolData b left outer join
MasterLockInsNew a on
b.id = a.id
where
a.id is null
Simple subquery will do.
select * from InitialPolData
where id not in (
select id from MasterLockInsNew
);
Try using NOT EXISTS:
SELECT Distinct i.*
FROM InitialPolData AS i
WHERE NOT EXISTS (SELECT 1
FROM MasterLockInsNew AS m
WHERE m.ID = i.ID)

Joining two tables based on three columns

For MS-Access, how do I accomplish following. I was thinking of writing VBA loop but I think it will take a while.
Here are the two tables:
Table A
| id | Day | Month | F_value1
---------------------------------------
| 1 | 10 | 11 | 523
| 1 | 11 | 11 | 955
| 2 | 1 | 11 | 45
| 2 | 2 | 11 | 49
Table B
| id | Day | Month | G_value1
---------------------------------------
| 1 | 10 | 11 | 19923
| 1 | 11 | 11 | 55455
| 2 | 1 | 11 | 45454
What I need:
| id | Day | Month | F_value1 | G_value1
-----------------------------------------------
| 1 | 10 | 11 | 523 | 19923
| 1 | 11 | 11 | 955 | 55455
| 2 | 1 | 11 | 45 | 45454
| 2 | 2 | 11 | 49 | Null
I tried Access Query designer but I had no luck. I'm not sure how to go about it in SQL. I already have table setup.
For programming way, I'm thinking
for each row in Table A
for each row in Table B
If TableA.fields = TableB.fields
Then Insert it into new table
End loop
End loop
You need multiple conditions for the joins. Fortunately, MS Access supports this with LEFT JOIN:
SELECT a.id, a.Day, a.Month, a.F_value1, b.G_Value1
FROM TableA as a LEFT JOIN
TableB as b
ON a.ID = b.ID AND a.day = b.day AND a.month = b.month;
You can use INSERT to insert into an existing table; INTO to create a new table. Or just run the query to get the results.
In SQL View, this should work and ideally be quicker than your suggested loop
SELECT a.*, b.G_Value1
INTO TableC
FROM TableA a
LEFT JOIN TableB b
ON a.ID=b.ID
If you need full join (ie all records of A and all records of B:
SELECT A.ID, A.Day, A.Month, A.F_value1, B.G_value1
FROM A LEFT JOIN B ON (A.Month= B.Month) AND (A.Day= B.Day) AND (A.ID = B.ID)
UNION
SELECT B.ID, B.Day, B.Month, A.F_value1, B.G_value1
FROM B LEFT JOIN A ON (B.ID = A.ID) AND (B.Day= A.Day) AND (B.Month= A.Month);