LEFT JOIN BETWEEN TWO TABLES - mysql

I have two tables X AND Y.i Have two coloumns in table X namely A and B and coloumn C in table Y.
Now i want to join X and Y using Left join With ON Condition
X.a=y.c or X.b=y.c. I want to get rows for 'c' coloumn in table Y with respective to rows for 'a 'coloumn in table X and 'c' cOLOUMN in table Y with respective to rows for B'coloumn in table X .
Result should be like :
-----------------------
X.a Y.C X.B Y.C
1 1 5 5
2 2 10 10
3 3 20 20
NULL NULL NULL NULL

You can simply do this
SELECT x.a, y1.c ac, x.b, y2.c bc
FROM x
LEFT JOIN y y1 ON x.a = y1.c
LEFT JOIN y y2 ON x.b = y2.c
Sample output:
| A | AC | B | BC |
|--------|--------|--------|--------|
| 1 | 1 | 5 | 5 |
| 2 | 2 | 10 | 10 |
| 3 | 3 | 20 | 20 |
| (null) | (null) | (null) | (null) |
Here is SQLFiddle demo

SELECT X.a, X.b, Y.c
FROM X LEFT JOIN Y
ON X.a = Y.c OR X.b = Y.c
Where's your problem ?
Is the problem not knowing sql syntax ? otherwise you're probably not framing your problem statement correctly
Based on your comment I'm going with the assumption that you want to know for which match is the value being retrieved and the modified query is :
SELECT X.a, X.b, Y.c,
CASE WHEN (X.a = Y.c AND X.b != Y.c) THEN 'a'
WHEN (X.a != Y.c AND X.b = Y.c) THEN 'b'
WHEN (X.a = Y.c AND X.b = Y.c) THEN 'ab' END AS 'FromColumn'
FROM X LEFT JOIN Y
ON X.a = Y.c OR X.b = Y.c

Related

SQL query for joining four tables

I am a newbie to writing SQL queries, Can anyone please help me write SQL query for below conditions and joins?
I have 4 tables let's say T1, T2, T3 and T4
T1 have columns a1(PK), a2, a3, a4
T2 have columns b1(PK), a1(FK), b2
T3 have columns c1(PK), a1(FK)
T4 have columns d1(PK), c1(FK), d2, d3
Conditions :
I want to have all columns from T1 when a4 == "xx"
If T1.a1 == T2.a1 and T1.a2 == "x", then I want b2 to be included in the final result as new column.
If T3.c1 == T4.c1 and T4.d2 == "y", then take value from d3 which needs to be joined to step4
If T1.a1 == T3.a1 and T1.a2 == "z", then value from d3 (step 3) should be included in the final result's new column
I am trying to solve as below, but I dont know how to add value d3 from step 3 to b2 in the final result.
select T1.a1,T1.a2,T1.a3,T1.a4,T2.b2 from
T1
left join T2 on T1.a1 == T2.a1 AND T1.a2 == "x"
left join (
select T3.a1,T4.d3 from T3,T4
where T3.c1 == T4.c1 AND T4.d2 == "y")
) joined on joined.a1 == T1.a1 and T1.a2 == "z"
where a4 == "xx";
Sample data
T1 table :
a1 | a2 | a3 | a4 |
1 | x | cat| xx |
2 | aa | hat| la |
3 | z | mat| xx |
T2 table :
b1 | b2 | a1 |
11 | 984 | 1 |
22 | 234 | 2 |
T3 table :
c1 | a1 |
111 | 3 |
222 | 7 |
T4 table :
d1 | d2 | d3 | c1 |
1111 | y | 100 | 111 |
2222 | yy | 200 | 333 |
Expected Result :
a1 | a2 | a3 | a4 | new column
1 | x | cat | xx | 984 (from T2.b2)
3 | z | mat | xx | 100 (from T4.d3)
Please help me to correct my query. Appreciate your help.
Assuming you want no results when your conditions are not met, this query will give you the results you want. It uses a CASE expression to select the correct value for new column, using the value of a2 to decide whether to use b2 or d3 for that value. Rows which don't meet your conditions are excluded by the WHERE clause.
SELECT T1.*,
CASE WHEN T1.a2 = 'x' THEN T2.b2
WHEN T1.a2 = 'z' THEN T4.d3
END AS `new column`
FROM T1
LEFT JOIN T2 ON T2.a1 = T1.a1
LEFT JOIN T3 ON T3.a1 = T1.a1
LEFT JOIN T4 ON T4.c1 = T3.c1
WHERE a4 = 'xx' AND (a2 = 'x' OR a2 = 'z' AND d2 = 'y')
ORDER BY a1
Output:
a1 a2 a3 a4 new column
1 x cat xx 984
3 z mat xx 100
Demo on dbfiddle
You can write this as:
SELECT T1.*,
(CASE WHEN T1.a2 = 'x'
THEN (SELECT T2.b2
FROM T2
WHERE T2.a1 = T1.a1
)
ELSE (SELECT T4.d3
FROM T3 JOIN
T4
ON T4.c1 = T3.c1
WHERE T3.a1 = T1.a1 AND T4.d2 = 'y'
)
END) as new_column
FROM T1
WHERE T1.a4 = 'xx' AND T1.a2 IN ('x', 'z')
ORDER BY T1.a1;
This assumes that at most one row matches from each of the two possibilities.
If so, I find that the logic here better captures your intent.

MySQL select all left entries from a table which is joined from another table

I have the following MySQL-Statement:
SELECT norm.NormID, norm.NormName
FROM (assignment
INNER JOIN norm
ON assignment.NID = norm.NormID )
INNER JOIN wire
ON assignment.LID = wire.WireID
WHERE wire.WireID= 109
ORDER BY norm.NormName;
Now what I got are the entries from the table assignment with the NormID and NormName for that WireID.
What I want to get are the entries from the table norm, which are not setted for this WireID.
E.g.:
WireID has the norm assignment A, B, D, G.
The table norm has the entries A, B, C, D, E, F, G, H.
What I want to get from the MySQL-Statment are the entries C, E, F, H.
How can I select those left norm entries for this WireID?
With the above statement I would get:
-----------------------
| NormID | NormName |
-----------------------
| 1 | A |
| 2 | B |
| 4 | D |
| 7 | G |
-----------------------
I want to have this Table:
-----------------------
| NormID | NormName |
-----------------------
| 3 | C |
| 5 | E |
| 6 | F |
| 8 | H |
-----------------------
I think (if I understood what you asked) you can try this :
SELECT norm.NormID, norm.NormName
FROM assignment
INNER JOIN norm ON assignment.NID = norm.NormID
LEFT JOIN wire ON assignment.LID = wire.WireID
WHERE assignment.LID= 109
AND wire.wireID IS NULL
ORDER BY norm.NormName;
Edit after your comments.
I think you could use:
SELECT A.NormID, A.NormName
FROM norm A
LEFT JOIN (SELECT NID FROM assignment WHERE LID = 109) B ON B.NID = A.NormID
WHERE B.NID IS NULL
ORDER BY A.NormName;
OR
SELECT A.NormID, A.NormName
FROM norm A
WHERE NOT EXISTS (SELECT 1 FROM assignment WHERE LID = 109 AND ASSIGNMENT.NID = A.NormID)
ORDER BY A.NormName;
Try this:
select norm.NormID,norm.NormName from norm
Inner JOIN
assignment on assignment.NID = norm.NormID
where assignment.LID in(select wireID from Wire where WireID = 109)
Im not so sure coz i dont have your data
after you added a sample data the entries that are not setted with 109 wireid are these:
SELECT norm.NormID, norm.NormName
FROM assignment
inner JOIN norm
ON assignment.NID = norm.NormID
INNER JOIN wire
ON assignment.LID = wire.WireID
WHERE wire.WireID <> 109
ORDER BY norm.NormName;

SQL Aggregation with SUM, GROUP BY and JOIN (many-to-many)

Here's an example Table layout:
TABLE_A: TABLE_B: TABLE_A_B:
id | a | b | c id | name a_id | b_id
--------------------- --------- -----------
1 | true | X | A 1 | A 1 | 1
2 | true | Z | null 2 | B 1 | 2
3 | false | X | null 3 | C 2 | 2
4 | true | Y | Q 4 | 1
5 | false | null | null 4 | 2
5 | 1
Possible Values:
TABLE_A.a: true, false
TABLE_A.b: X, Y, Z
TABLE_A.c: A, B, C, ... basically arbitrary
TABLE_B.name: A, B, C, ... basically arbitrary
What I want to achieve:
SELECT all rows from TABLE_A
SUM(where a = true),
SUM(where a = false),
SUM(where b = 'X'),
SUM(where b = 'Y'),
SUM(where b = 'Z'),
SUM(where b IS NULL),
and also get the SUMs for all distinct TABLE_A.c values.
and also get the SUMs for all those TABLE_A_B relations.
The result for the example Table above should look like:
aTrue | aFalse | bX | bY | bZ | bNull | cA | cQ | cNull | nameA | nameB | nameC
-------------------------------------------------------------------------------
3 | 2 | 2 | 1 | 1 | 1 | 1 | 1 | 3 | 3 | 3 | 0
What I've done so far:
SELECT
SUM(CASE WHEN a = true THEN 1 ELSE 0 END) AS aTrue,
SUM(CASE WHEN b = false THEN 1 ELSE 0 END) AS aFalse,
SUM(CASE WHEN b = 'X' THEN 1 ELSE 0 END) AS bX,
...
FROM TABLE_A
What's my problem?
Selecting column TABLE_A.a and TABLE_A.b is easy, because there's a fixed number of possible values.
But I can't figure out how to count the distinct values of TABLE_A.c. And basically the same problem for the JOINed TABLE_B, because the number of values within TABLE_B is unknown and can change over time.
Thanks for your help! :)
EDIT1: New (preferred) SQL result structure:
column | value | sum
----------------------------
TABLE_A.a | true | 3
TABLE_A.a | false | 2
TABLE_A.b | X | 2
TABLE_A.b | Y | 1
TABLE_A.b | Z | 1
TABLE_A.b | null | 1
TABLE_A.c | A | 1
TABLE_A.c | Q | 1
TABLE_A.c | null | 3
TABLE_B.name | A | 3
TABLE_B.name | B | 3
TABLE_B.name | C | 0
From your original request of rows as a simulated pivot. By doing a SUM( logical condition ) basically returns 1 if true, 0 if false. So, since the column "a" is true or false, simple sum of "a" or NOT "a" (for the false counts -- NOT FALSE = TRUE). Similarly, your "b" column, so b='X' = true counted as 1, else 0.
In other sql engines, you might see it as SUM( case/when ).
Now, since your table counts don't rely on each other, they can be separate SUM() into their own sub-alias query references (pqA and pqB for pre-queryA and pre-queryB respectively). Since no group by, they will each result in a single row. With no join will create a Cartesian, but since 1:1 ratio, will only return a single record of all columns you want.
SELECT
pqA.*, pqB.*
from
( SELECT
SUM( ta.a ) aTrue,
SUM( NOT ta.a ) aFalse,
SUM( ta.b = 'X' ) bX,
SUM( ta.b = 'Y' ) bY,
SUM( ta.b = 'Z' ) bZ,
SUM( ta.b is null ) bNULL,
SUM( ta.c = 'A' ) cA,
SUM( ta.c = 'Q' ) cQ,
SUM( ta.c is null ) cNULL,
COUNT( distinct ta.c ) DistC
from
table_a ta ) pqA,
( SELECT
SUM( b.Name = 'A' ) nameA,
SUM( b.Name = 'B' ) nameB,
SUM( b.Name = 'C' ) nameC
from
table_a_b t_ab
join table_b b
ON t_ab.b_id = b.id ) pqB
This option gives your second (preferred) output
SELECT
MAX( 'TABLE_A.a ' ) as Basis,
CASE when a then 'true' else 'false' end Value,
COUNT(*) finalCnt
from
TABLE_A
group by
a
UNION ALL
SELECT
MAX( 'TABLE_A.b ' ) as Basis,
b Value,
COUNT(*) finalCnt
from
TABLE_A
group by
b
UNION ALL
SELECT
MAX( 'TABLE_A.c ' ) as Basis,
c Value,
COUNT(*) finalCnt
from
TABLE_A
group by
c
UNION ALL
SELECT
MAX( 'TABLE_B.name ' ) as Basis,
b.Name Value,
COUNT(*) finalCnt
from
table_a_b t_ab
join table_b b
ON t_ab.b_id = b.id
group by
b.Name
I think You will need to build dynamic query as you don't know possible values for column C in table A. So you can write store procedure where you can get list of distinct value for Column C in one variable and by using "Do WHILE" you can construct your dynamic query.
Please let me know if you need more help in detail
Dynamic SQL

Compare variation between first and second row for group

I got a "Empresas" table
dbo.empresas
id | name | delegacion_id
-------------------------
1 | A | 3
2 | B | 3
3 | C | 3
4 | D | 4
a "pagos" table
dbo.pagos
id | id_empresa | monto | periodo
----------------------------------
1 | 1 | 120 | 2012-11-01
2 | 1 | 125 | 2012-12-01
3 | 2 | 150 | 2012-11-01
4 | 1 | 200 | 2013-01-01
5 | 2 | 151 | 2012-12-01
I have a value X that is a percentage.
I need to show the "empresas" that, comparing the "montos" of their two last "pagos" (ordered by periodo), have changed at least +X% or -X%, from an especific id_delegacion
For example, if we run this query with these example values, considering
X = 10
id_delegacion = 3
the output expected will be:
name | periodo | monto
---------------------------
A | 2012-12-01 | 125
A | 2013-01-01 | 200
empresa A is from delegacion_id = 3, and the comparison between the last two pagos, ordered by periodo desc (200 => 125) is bigger than 10%.
B is not showed because the comparison is smaller than 10%.
C is not showed because has no row in "pagos" table
D is from another delegation.
How can I get this desired output? For the record, using MySQL 5.5.8.
What I've done
I got this
select P.id_empresa, max(periodo) as periodo from
pagos P
where id_empresa in(
select e.id
from empresa E
where E.id_delegacion = 3
)
group by p.id_empresa, p.periodo
having count(*) > 1
with these I got the "empresas" that have more than one "pago" row, and got id_delegation = 3.
Also get the first period (the maximum), but I don't know how to get the second for each empresa, and compare them.
thanks
This is my query:
SELECT
empresas.name,
pagos.periodo,
pagos.monto
FROM
pagos INNER JOIN (
SELECT
lst.id id1,
prc.id id2
FROM (
SELECT
p1.id_empresa,
MAX(p1.periodo) last_p,
MAX(p2.periodo) prec_p
FROM
pagos p1 INNER JOIN pagos p2
ON p1.id_empresa = p2.id_empresa
AND p2.periodo < p1.periodo
GROUP BY
id_empresa) latest
INNER JOIN
pagos lst ON lst.id_empresa = latest.id_empresa AND lst.periodo=latest.last_p
INNER JOIN
pagos prc ON prc.id_empresa = latest.id_empresa AND prc.periodo=latest.prec_p
WHERE
lst.monto > prc.monto * 1.1) ids
ON pagos.id IN (ids.id1, ids.id2)
INNER JOIN
empresas
ON pagos.id_empresa = empresas.id
WHERE
delegacion_id=3
I think it can be simplified if you want to have values on the same row, e.g.
name | ultimo_periodo | ultimo_monto | anterior_periodo | anterior_monto
Please see fiddle here.
I still wondering if it can be simplified a little, but I am not sure if it is. Here's another solution:
SELECT
empresas.name,
pagos.periodo,
pagos.monto
FROM
pagos INNER JOIN empresas
ON pagos.id_empresa = empresas.id
INNER JOIN (
SELECT
id_empresa,
MAX(CASE WHEN row=1 THEN monto END) lst_monto,
MAX(CASE WHEN row=2 THEN monto END) prc_monto,
MAX(id) id1, MIN(id) id2
FROM (
SELECT
p1.*, COUNT(*) row
FROM
pagos p1 INNER JOIN pagos p2
ON p1.id_empresa = p2.id_empresa
AND p1.periodo <= p2.periodo
INNER JOIN empresas
ON p1.id_empresa = empresas.id
WHERE
empresas.delegacion_id = 3
GROUP BY
p1.id, p1.id_empresa, p1.monto, p1.periodo
HAVING
COUNT(*)<=2
ORDER BY
p1.id_empresa, p1.periodo desc
) s
GROUP BY
id_empresa
HAVING
lst_monto>prc_monto*1.1
) l ON pagos.id IN (l.id1, l.id2)
Please see fiddle here.

How to create left joins without repetition of rows on the left side

I have a scenario where there are two tables (tables A and B) linked in a one to many relationship. For a row in table A, the maximum number of linked rows in B is two, and these two rows (if they exist) are different from each other through a type column whose value is either x or y.
Aid | Name Bid | type | Aid
1 | name1 1 | x | 1
2 | name2 2 | x | 2
3 | name3 3 | y | 2
Now, what I want is to have a join query for the two tables in such a way that all rows in A will be displayed (no repetition) and two columns called type x and type y will hold a boolean / integer value to show the existence of types x and y for each row in A. i.e,
Aid | Name | Type X | Type Y |
1 | name1 | X | NULL |
2 | name2 | X | Y |
3 | name3 | NULL | NULL |
My DBMS is MySql.
Thanks.
You have to use two joins:
SELECT A.*, b1.type AS typeX, b2.type as typeY
FROM A
LEFT JOIN B b1
ON A.aid = b1.aid
AND b1.type = 'x'
LEFT JOIN B b2
ON a.aid = b2.aid
AND b2.type = 'y'
Well, this happens because your second table uses the EAV-model. If you had two tables, one for type_x and one for type_y, your relational schema would be a lot cleaner.
Offcourse, EAV does work, be it more clumsily:
SELECT a.aid, a.name, bx.type, by.type
FROM table_a a
LEFT JOIN table_b bx
ON a.aid = bx.aid
AND bx.type = 'x'
LEFT JOIN table_b by
ON a.aid = by.aid
AND by.type = 'y'