MS Access Full Outer Join using 2 fields? - ms-access

I have 2 tables that I want to do a FULL OUTER JOIN on in MS Access.
Say I have Table A which looks like this:
A.ID | A.Value | A.DATE
--------+-----------+----------
1 | 30 | 05/2018
1 | 28 | 06/2018
1 | 26 | 07/2018
2 | 250 | 04/2018
2 | 252 | 05/2018
2 | 240 | 06/2018
And Table B which looks like this:
B.ID | B.FCST | B.OUTDATE
--------+-----------+-----------
1 | 35 | 06/2018
1 | 33 | 07/2018
1 | 30 | 08/2018
2 | 300 | 06/2018
2 | 280 | 07/2018
2 | 260 | 08/2018
And I need to perform Joins and Unions to achieve this:
A.ID | A.Value | A.DATE | B.FCST | B.OUTDATE
--------+-----------+---------+-----------+------------
1 | 30 | 05/2018 | - | -
1 | 28 | 06/2018 | 35 | 06/2018
1 | 26 | 07/2018 | 33 | 07/2018
1 | - | - | 30 | 08/2018
2 | 250 | 04/2018 | - | -
2 | 252 | 05/2018 | - | -
2 | 240 | 06/2018 | 300 | 06/2018
2 | - | - | 280 | 07/2018
2 | - | - | 260 | 08/2018
So I need to do an Inner Join with A.ID = B.ID, AND A.DATE = B.OUTDATE, and then somehow get "earlier" data from Table.A to "Sit on top" of the inner joined data, and the "later" data from Table.B to do the opposite. This is my attempt so far:
Select A.ID, A.Value, A.DATE, B.FCST, B.OUTDATE
FROM Table.A JOIN Table.B ON A.ID = B.ID AND A.DATE = B.OUTDATE
UNION ALL
Select A.ID, A.Value, A.DATE, B.FCST, B.OUTDATE
FROM Table.A LEFT JOIN Table.B ON A.ID = B.ID;
WHERE B.ID IS NULL
UNION ALL
Select A.ID, A.Value, A.DATE, B.FCST, B.OUTDATE
FROM Table.A RIGHT JOIN Table.B ON A.ID = B.ID
WHERE A.ID IS NULL
ORDER BY A.ID ASC;
But I've missed the mark it appears. I'm getting duplicate lines, and it just looks like an Inner Join. I will gladly take any advice as to help get this right.

I would suggest a union of two left joins to give the same result as full outer, and finally with a touch of sorting to yield the desired ordering:
select c.* from
(
select a.id, a.value, a.date, b.fcst, b.outdate
from a left join b on a.id = b.id and a.date = b.outdate
union
select b.id, a.value, a.date, b.fcst, b.outdate
from b left join a on a.id = b.id and a.date = b.outdate
) c
order by c.id, nz(c.date, c.outdate)

Related

How to remove duplicate/inverted rows from INNER JOIN

I am using SQL to pull from a table, I am also using an INNER JOIN to select to rows with linking values/columns, and appending them into one big row.
Table below:
| name | value | num |
| James | HEX124 | 1 |
| James | JEU836 | 4 |
I am now joining these two rows, into one row using this SQL:
SELECT a.name, a.value, a.num, b.name, b.value, b.num
FROM MY_TABLE a
INNER JOIN MY_TABLE b ON a.name = b.name //Inner joining where the name is the same
WHERE a.value <> b.value // where the values are NO the same
Result:
| a.name | a.value | a.num | b.name | b.value | b.num |
| James | HEX124 | 1 | James | JEU836 | 4 |
| James | JEU836 | 4 | James | HEX124 | 1 |
As you can see in the result, this is working, but it's returning every possible result, I want to only return one of these rows, as its duplicated/inverted them almost.
Maybe by returning only the first row WHERE a.name is a duplicate?
Desired Result:
| a.name | a.value | a.num | b.name | b.value | b.num |
| James | HEX124 | 1 | James | JEU836 | 4 |
Thank you
Simply change:
a.value <> b.value
to:
a.value < b.value
I would put the condition in the ON clause, like this:
SELECT a.name, a.value, a.num, b.name, b.value, b.num
FROM MY_TABLE a INNER JOIN
MY_TABLE b
ON a.name = b.name AND a.value < b.value;

MySQL : Select the rows having the max value for one field and keep correct values for joining tables

I know there are a lot of already answered questions about this but I spent days to adapt the answers to my case and I can't manage to make it work. So please be kind and help me.
I do this query :
SELECT
a.id,
a.commit,
b.id,
c.value
FROM TableA a
INNER JOIN TableC c ON c.id_a = a.id
INNER JOIN TableB ON c.id_b = b.id
INNER JOIN TableD d ON a.id_d = d.id
WHERE a.commit <= 9000 AND a.id_s = 10 AND d.id_f = 1
ORDER BY b.custom_order, a.commit ASC
Below, the results of this query. I want to get only the rows that have the greatest commit for every b.id :
a.id | a.commit | b.id | c.value
--------------------------------
257 | 4000 | 11 | 33
258 | 6000 | 11 | 34
259 | 8000 | 11 | 35
257 | 4000 | 1 | 40
258 | 6000 | 1 | 40
259 | 8000 | 1 | 40
257 | 4000 | 2 | 40
258 | 6000 | 2 | 40
259 | 8000 | 2 | 40
257 | 4000 | 3 | 15
258 | 6000 | 3 | 25
259 | 8000 | 3 | 25
So, I want all the rows with commit = 8000 :
a.id | a.commit | b.id | c.value
--------------------------------
259 | 8000 | 11 | 35
259 | 8000 | 1 | 40
259 | 8000 | 2 | 40
259 | 8000 | 3 | 25
I found this solution below but I can't use it with Symfony 3.4 and Doctrine because Doctrine can't do an inner join on a sub query...
SELECT
a1.id,
a1.commit,
b1.id,
c1.value
FROM TableA a1
INNER JOIN TableC c1 ON c1.id_a = a1.id
INNER JOIN TableB ON c1.id_b = b1.id
INNER JOIN TableD d1 ON a1.id_d = d1.id
INNER JOIN(
SELECT
b.id,
MAX(a.commit) max_commit
FROM TableA a
INNER JOIN TableC c ON c.id_a = a.id
INNER JOIN TableB ON c.id_b = b.id
INNER JOIN TableD d ON a.id_d = d.id
WHERE a.id_s = 10 AND a.commit <= 9000 AND d.id_f = 1
GROUP BY b.id
) results ON b1.id = results.id AND a1.commit = results.max_commit
WHERE a1.id_s = 10
ORDER BY b1.custom_order ASC
I don't know about Doctrine but you can use the ROW_NUMBER() window function to filter out the rows you don't want. For example:
SELECT
a_id,
commit,
b_id,
value
from (
SELECT
a.id as "a_id",
a.commit,
b.id as "b_id",
c.value,
b.custom_order,
row_number() over(PARTITION BY b.id ORDER BY a.commit DESC) as rn
FROM TableA a
INNER JOIN TableC c ON c.id_a = a.id
INNER JOIN TableB ON c.id_b = b.id
INNER JOIN TableD d ON a.id_d = d.id
WHERE a.commit <= 9000 AND a.id_s = 10 AND d.id_f = 1
) x
ORDER BY custom_order, commit ASC
WHERE rn = 1

Mysql select with multiple condition

I would need help creating a query
Table A
+------------+--------------------+-------+
| product_id | name | price |
+------------+--------------------+-------+
| 13 | Product 13 | 5 |
| 14 | Product 14 | 2 |
| 15 | Product 15 | 3 |
| 16 | Product 16 | 2 |
| 17 | Product 17 | 15 |
+------------+--------------------+-------+
Table B
+----+------------+-------------+
| id | product_id | taxonomy_id |
+----+------------+-------------+
| 10 | 13 | 5 |
| 11 | 13 | 2 |
| 12 | 14 | 3 |
| 13 | 15 | 2 |
| 14 | 16 | 15 |
| 14 | 16 | 5 |
| 14 | 16 | 19 |
| 14 | 16 | 21 |
| 14 | 16 | 18 |
+----+------------+-------------+
my attempt
SELECT *
FROM A
LEFT JOIN B ON B.product_id = A.product_id
WHERE IF(B.taxonomy_id IN ('5','15'),
IF(B.taxonomy_id IN ('2'), 1, 0), 0) = 1
GROUP BY A.product_id
I need it to give me back those results from table A for which it is true
B.taxonomy_id is "5" OR "15" and B.taxonomy_id is "2"
The result would be for this example -> product_id - 13
and I also need to get a number of results SELECT count(*) ... -> return is 1
Is it normal that your tables don't have an id column as a unique primary key ?
Anyway, here is what I came across, tell me if it works :
SELECT table_nameA.product_id
FROM table_nameA
LEFT JOIN table_nameB on table_nameA.product_id = table_nameB.product_id
WHERE taxonomy_id = 2 AND table_nameA.product_id IN
(SELECT table_nameA.product_id
FROM table_nameA
LEFT JOIN table_nameB on table_nameA.product_id = table_nameB.product_id
where taxonomy_id = 5 or taxonomy_id = 15
GROUP BY table_nameA.product_id, taxonomy_id)
Result is :
| product_id |
|------------|
| 13 |
About your count query, it is exactly the same.
SELECT count(table_nameA.product_id) as Quantity
FROM table_nameA
LEFT JOIN table_nameB on table_nameA.product_id = table_nameB.product_id
WHERE taxonomy_id = 2 AND table_nameA.product_id IN
(SELECT table_nameA.product_id
FROM table_nameA
LEFT JOIN table_nameB on table_nameA.product_id = table_nameB.product_id
where taxonomy_id = 5 or taxonomy_id = 15
GROUP BY table_nameA.product_id, taxonomy_id)
Result is :
| Quantity |
|----------|
| 1 |
Instead of doing filtering in the WHERE clause; you need to do this conditional filtering inside the HAVING clause. You can avoid LEFT JOIN, as the product should have taxonomies for them (2 AND (5 or 15)):
SELECT a.product_id
FROM tablea a
JOIN tableb b on b.product_id = a.product_id
GROUP BY a.product_id
HAVING SUM(b.taxonomy_id IN (5,15))
AND SUM(b.taxonomy_id = 2)
Result
| product_id |
| ---------- |
| 13 |
View on DB Fiddle
You can use exists:
select a.*
from a
where exists (select 1
from b
where b.product_id = a.product_id and
b.taxonomy_id in (5, 15)
) and
exists (select 1
from b
where b.product_id = a.product_id and
b.taxonomy_id in (2)
) ;
If you just wanted the product_ids, then I would recommend aggregation:
select b.product_id
from b
where b.taxonomy_id in (2, 5, 15)
group by b.product_id
having sum( b.taxonomy_id in (5, 15) ) > 0 and
sum( b.taxonomy_id in (2) ) > 0 ;
You can use either join with having clause or intersect function with 2 queries to get the output
13
in your senario.
Join with having clause:
select a.product_id from a inner join b on a.product_id = b.product_id group by a.product_id having (SUM (b.taxonomy_id IN (5,15)) and SUM (b.taxonomy_id in (2)));
Intersect with 2 queries:
select a.product_id from a where (a.product_id IN (select product_id from b where b.taxonomy_id = 2))
INTERSECT
select a.product_id from a where (a.product_id IN (select product_id from b where b.taxonomy_id in (5,15)));
For count use something like this which will return
1
as an output:
select COUNT(*) from (select a.product_id from a where (a.product_id IN (select product_id from b where b.taxonomy_id = 2))
INTERSECT
select a.product_id from a where (a.product_id IN (select product_id from b where b.taxonomy_id in (5,15)))) I;

MySQL select rows where its columns sum equal value

I have following tables:
A:
+----+-----------+-------+----------+
| ID | PaymentID | Price | Quantity |
+----+-----------+-------+----------+
| 1 | 1 | 128 | 1 |
| 2 | 2 | 10 | 2 |
| 3 | 2 | 11 | 1 |
| 4 | 3 | 100 | 2 |
+----+-----------+-------+----------+
B:
+-----------+------------+
| PaymentID | TotalPrice |
+-----------+------------+
| 1 | 128 |
| 2 | 31 |
| 3 | 201 |
+-----------+------------+
And query:
SELECT a.ID
FROM a
LEFT JOIN b ON b.PaymentID = a.PaymentID
WHERE b.TotalPrice = (a.Price * a.Quantity)
It works fine when a.PaymentID is unique, but some transactions in table A are separated and paid (table B) together. Query above return a.ID = 1 but I need to return a.ID = 1,2,3.
a.PaymentID(1): 128 * 1 = 128 MATCH
a.PaymentID(2): 10 * 2 + 11 * 1 = 31 MATCH
a.PaymentID(3): 100 * 2 = 200 NOT MATCH
SQL Fiddle
You are trying to join sum of Price and amount from table a to table b along with the PaymentId, and using it onto a joining clause which would be calculated per row based not on aggregate based.
You may need to first find the aggregate part and then join something as
select
a.ID
from a
left join (
select sum(Price*Quantity) as tot,PaymentID
from a group by PaymentID
)x on x.PaymentID = a.PaymentID
join b on b.PaymentID = a.PaymentID and x.tot = b.TotalPrice
http://www.sqlfiddle.com/#!9/3b261/45
Try this statement:
SELECT a.ID, b.totalprice
FROM a
LEFT JOIN b ON b.PaymentID = a.PaymentID
group by b.paymentID
having TotalPrice = sum(a.Price * a.Quantity)
SQLFIDDLE
UPDATE: After clarification:
select a.id from a where paymentId in(
select paymentID from(
SELECT a.paymentID as paymentID, b.totalprice
FROM a
LEFT JOIN b ON b.PaymentID = a.PaymentID
group by b.paymentID
having TotalPrice = sum(a.Price * a.Quantity)) as c )

join two tables(one of them is part of the other)

I have two tables Table_A and Table_B and i want to join those tables to optain Table_c as the result:
Table_A:
+-----------+-----------+---------+
| tableA_id | tableB_id | v_id |
+-----------+-----------+---------+
| 1 | 2 | 27 |
| 2 | 3 | 27 |
| 3 | 3 | 28 |
| 4 | 1 | 26 |
| 5 | 2 | 26 |
| 6 | 3 | 26 |
| 7 | 1 | 24 |
| 8 | 1 | 25 |
+-----------+-----------+---------+
Table_B:
+-----------+-----------+
| tableB_id | s_name |
+-----------+-----------+
| 1 | s1 |
| 2 | s2 |
| 3 | s3 |
+-----------+-----------+
Table_c:
+-----------+-----------+-----------++--------+
| tableB_id | s_name | tableA_id | v_id |
+-----------+-----------+-----------+---------+
| 1 | s1 | null | null |
| 2 | s2 | 1 | 27 |
| 3 | s3 | 2 | 27 |
+-----------+-----------+-----------+---------+
I tried different queries, but i couldn't reach the desired output.
This is MYSQL query: *Edit: reverse table order.
SELECT s.tableB_id, s.s_name, v.tableA_id, v.v_id
FROM Table_B as s
left OUTER JOIN Table_A as v
ON v.v_id=27
EDIT:
The result should be all Table_B data on left and assign to it table_A data if any or make it null. How can i make this?
Last Edit:
Here is a solution i came up with:
SELECT s.tableB_id , s.s_name, v.tableA_id, v.v_id
FROM Table_B s , Table_A v
WHERE v.v_id=27 AND v.tableB_id = s.tableB_id
UNION
SeLECT s.tableB_id , s.s_name, null as tableA_id, null as v_id
FROM Table_B s
WHERE s.tableB_id NOT IN (SELECT s.tableB_id
FROM Table_B s , Table_A v
WHERE v.v_id=27 AND v.tableB_id = s.tableB_id )
You could try:
SELECT tb.tableB_id, tb.s_name, ta.tableA_id, ta.v_id
FROM Table_B tb LEFT JOIN Table_A ta
ON tb.tableB_id = ta.tableB_id
To choose only one you could use:
SELECT tb.tableB_id, tb.s_name, ta.tableA_id, ta.v_id
FROM Table_B tb LEFT JOIN
(SELECT * FROM Table_A
WHERE v_id = 27) ta
ON tb.tableB_id = ta.tableB_id
SELECT distinct s.tableB_id, s.s_name, v.tableA_id, v.v_id
FROM Table_A as v
left OUTER JOIN Table_B as s
ON v.tableB_id =s.tableB_id and v.v_id = 27;
use this query hope fully you will get result
SELECT s.tableB_id, s.s_name, v.tableA_id, v.v_id
FROM Table_A as v
left OUTER JOIN Table_B as s ON s.tableB_id=v.tableB_id
WHERE v.v_id=27
Reverse the order, making it b LEFT JOIN a and add the condition for the common column (tableB_id):
SELECT s.tableB_id, s.s_name, v.tableA_id, v.v_id
FROM Table_B as s
LEFT OUTER JOIN Table_A as v
ON v.tableB_id = s.tableB_id
AND v.v_id=27 ;