How to manage COUNT, GROUP BY and HAVING? - sql

I'm not experimented in SQL and I try maybe to do something which is impossible. SQL give this sensation it is possible to do it on only 1 request, but maybe not...
I have 4 joined tables A -> S -> T -> F.
A simple request give me these data :
select a.id, s.id, t.type, f.name
from table_a a
inner join table_s s on a.id = s.id
inner join table_t t on t.id = s.t_id
inner join table_f f on f.id = t.f_id
where f.name = 'C';
a.id | s.id | t.type | f.name
-----------------------------
1 | 1 | E | C
1 | 2 | R | C
2 | 3 | E | C
3 | 4 | R | C
I would like to find ALL A ids which have multiple S rows associated.
And I would like to find ALL a ids which have only one S row of T type = R.
For the first one, I made this SQL query :
select a.id from table_a a
inner join table_s s on a.id = s.id
inner join table_t t on t.id = s.t_id
inner join table_f f on f.id = t.f_id
where f.name = 'C'
group by a.id
having count(s.*) > 1;
But now, for the second query I don't understand how to filter on t.type and count.
I try this request but the response is not good (a.id = 1 is returned)
select a.id from table_a a
inner join table_s s on a.id = s.id
inner join table_t t on t.id = s.t_id
inner join table_f f on f.id = t.f_id
where f.name = 'C'
group by a.id
having count(s.*) = 1 and t.type = 'R';
Any idea ?
Thank you

I would like to find ALL a ids which have only one S row of T type = R.
Does this do what you want?
select a.id from table_a a
inner join table_s s on a.id = s.id
inner join table_t t on t.id = s.t_id
inner join table_f f on f.id = t.f_id
where f.name = 'C'
group by a.id
having count(*) = 1 and min(t.type) = 'R'
This gives you groups that have only one row, whose type is "R".

Related

Conditional Left Join SQL

table A
----------------------------
NAME | CODE | BRANCH
----------------------------
bob | PL | B
david | AA | B
susan | PL | C
joe | AB | C
alfred | PL | B
table B
----------------------------
CODE | DESCRIPTION
----------------------------
PL | code 1
PB | code 2
PC | code 3
table C
----------------------------
CODE | DESCRIPTION
----------------------------
AA | code 4
AB | code 5
AC | code 6
Is there any way to join table A, B and C. without join all the table?
select A.*, COALESCE(B.DESCRIPTION, C.DESCRIPTION) AS DESCRIPTION from A
left join B on A.CODE = B.CODE
left join C on A.CODE = C.CODE
In my real case there will be more than 10 to join with the same column.
So I need conditional left join, something like this
SELECT A* , DESCRIPTION
FROM A LEFT JOIN (
CASE
WHEN A.CODE = 'B' THEN SELECT * FROM B
WHEN A.CODE = 'C' THEN SELECT * FROM C
END
) BC ON A.CODE = BC.CODE
You cannot use CASE to implement flow control. In SQL CASE is an expression that returns a single value.
You can instead use the following query:
select A.*,
CASE A.BRANCH
WHEN 'B' THEN B.DESCRIPTION
WHEN 'C' THEN C.DESCRIPTION
END AS DESCRIPTION
from A
left join B on A.CODE = B.CODE AND A.BRANCH = 'B'
left join C on A.CODE = C.CODE AND A.BRANCH = 'C'
You could use this to generate queries. Then you write a PL/SQL block to loop through all these queries and execute dynamically to give you separate results.
SELECT 'SELECT A.* , DESCRIPTION
FROM TABLEA A LEFT JOIN '
|| CASE WHEN A.BRANCH = 'B' THEN 'TABLEB B' END
|| CASE WHEN A.BRANCH = 'C' THEN 'TABLEC C' END
|| ' ON '
|| 'A.CODE = '
|| CASE WHEN A.BRANCH = 'B' THEN 'B.CODE' END
|| CASE WHEN A.BRANCH = 'C' THEN 'C.CODE' END
v_query
FROM TableA A;
Output
V_QUERY
--------------------------------------------------------------------------------
SELECT A.* , DESCRIPTION
FROM TABLEA A LEFT JOIN TABLEB B ON A.CODE = B.CODE
SELECT A.* , DESCRIPTION
FROM TABLEA A LEFT JOIN TABLEB B ON A.CODE = B.CODE
SELECT A.* , DESCRIPTION
FROM TABLEA A LEFT JOIN TABLEC C ON A.CODE = C.CODE
SELECT A.* , DESCRIPTION
FROM TABLEA A LEFT JOIN TABLEC C ON A.CODE = C.CODE
SELECT A.* , DESCRIPTION
FROM TABLEA A LEFT JOIN TABLEB B ON A.CODE = B.CODE

SQL Server, selecting from 2 columns from different tables

I have these columns from 2 tables
Table1 Table2
Code ID Code ID
A 1 A 1
B 1 B 1
C 1 C 1
D 1
E 1
My query:
Select
a.id, a.code, b.code
from
Table1 a, Table2 b
where
a.id = '1' and a.id = b.id
What I expected
ID code code
1 A A
1 B B
1 C C
1 D NULL
1 E NULL
What I got
ID code code
1 A A
1 B A
1 C A
1 D A
1 E A
1 A B
1 B B
1 C B
....
Any ideas? distinct didn't help
Thanks
Well, all the ID's in both tables are 1, so by joining on ID you'll get the cartesian product of both tables.
Instead, you'll need to do a left outer join based on Table1.Code:
Select a.id, a.code, b.code
from Table1 a LEFT OUTER JOIN Table2 b
on a.code = b.code
where a.id = '1';
You need to do a LEFT OUTER JOIN instead of a Cartesian Product
SELECT a.Id, a.Code, b.Code FROM Table1 a
LEFT OUTER JOIN Table2 b ON a.Code = b.Code
WHERE a.Id = '1'
A LEFT OUTER JOIN returns all rows from the left-hand side of the join (in this case Table 1) regardless of whether there is a matching record in the table on the right-hand side of the join (in this case Table 2). Where there is no match a NULL is returned for b.Code as per your requirements.
Reference OUTER JOINS

T-SQL Removing multiple LEFT JOIN

I have such query. It returns ColA and ColB from TableA and UserName from table Users. Then it displays several fields from TableB as additional columns to results. It works but is there any better way than using these multiple LEFT JOINS ?
SELECT a.COlA, a.ColB, u.UserName,
b1.Value,
b2.Value,
b3.Value,
b4.Value,
FROM TableA a JOIN Users u ON a.UserId = u.UserId
LEFT JOIN TableB b1 ON a.EventId = b1.EventId AND b1.Code = 5
LEFT JOIN TableB b2 ON a.EventId = b2.EventId AND b2.Code = 15
LEFT JOIN TableB b3 ON a.EventId = b3.EventId AND b3.Code = 18
LEFT JOIN TableB b4 ON a.EventId = b4.EventId AND b4.Code = 40
WHERE (a.UserId = 3) ORDER BY u.UserName ASC
TableB looks like:
Id | EventId | Code | Value
----------------------------
1 | 1 | 5 | textA
2 | 1 | 15 | textB
3 | 1 | 18 | textC
Sometimes Code is missing but for each event there are no duplicated Codes (so each LEFT JOIN is just another cell in the same result record).
I cannot understand why you want to change something that is working, but here's another way (which does those LEFT joins, but in a different way):
SELECT a.COlA, a.ColB, u.UserName,
( SELECT b.Value FROM TableB b WHERE a.EventId = b.EventId AND b.Code = 5 ),
( SELECT b.Value FROM TableB b WHERE a.EventId = b.EventId AND b.Code = 15 ),
( SELECT b.Value FROM TableB b WHERE a.EventId = b.EventId AND b.Code = 18 ),
( SELECT b.Value FROM TableB b WHERE a.EventId = b.EventId AND b.Code = 40 )
FROM TableA a JOIN Users u ON a.UserId = u.UserId
WHERE (a.UserId = 3)
ORDER BY u.UserName ASC
SELECT
a.COlA, a.ColB, u.UserName
,MAX(CASE WHEN b.Value = 5 THEN b.value ELSE 0 END) AS V5
,MAX(CASE WHEN b.Value = 15 THEN b.value ELSE 0 END) AS V15
,MAX(CASE WHEN b.Value = 18 THEN b.value ELSE 0 END) AS V18
,MAX(CASE WHEN b.Value = 40 THEN b.value ELSE 0 END) AS V45
,COUNT(CASE WHEN b.Value not IN (5,15,18,40) THEN 1 ELSE NULL END) AS CountVOther
FROM TableA a
INNER JOIN Users u ON a.UserId = u.UserId
LEFT JOIN TableB b ON (a.EventId = b.EventId)
WHERE (a.UserId = 3)
GROUP BY a.colA, a.colB, u.Username
ORDER BY u.UserName ASC

How to JOIN these 2 tables together (MySQL, Hierarchical query)?

I have a categories table that looks like this:
id | name | parent
-----------------------
1 | Toys | 1
2 | Clothing | 1
3 | Kid's Toys | 0
I have another table called category_relationships which looks like this:
id | category_id | parent_id
----------------------------
1 | 3 | 1
I want to have the following output:
Categories:
Toys
- Kid's Toys
Clothing
How to achieve this with one query?
A better/proper/robust answer will probably be create a MySQL PROCEDURE for this, but if your data can fit in these limitations, you can use the below:
no more than 5 levels (or expand the pattern as required)
IDs are no more than 6 digits (or change the concat expressions)
This query uses Concat to build a sortable reference so that children of A come after A etc. The names are indented manually using concat and leading spaces.
select concat(1000000 + a.id, '|') SORT
,a.name
from categories a
where a.parent = 1 # top level parents only
union all
select concat(1000000 + a.id, '|',
1000000 + IFNULL(b.id,0), '|')
,concat(' - ', b.name)
from categories a
inner join category_relationships a1 on a1.parent_id = a.id
inner join categories b on b.id = a1.category_id
where a.parent = 1
union all
select concat(1000000 + a.id, '|',
1000000 + IFNULL(b.id,0), '|',
1000000 + IFNULL(c.id,0), '|')
,concat(' - ', c.name)
from categories a
inner join category_relationships a1 on a1.parent_id = a.id
inner join categories b on b.id = a1.category_id
inner join category_relationships b1 on b1.parent_id = b.id
inner join categories c on c.id = b1.category_id
where a.parent = 1
union all
select concat(1000000 + a.id, '|',
1000000 + IFNULL(b.id,0), '|',
1000000 + IFNULL(c.id,0), '|',
1000000 + IFNULL(d.id,0), '|')
,concat(' - ', d.name)
from categories a
inner join category_relationships a1 on a1.parent_id = a.id
inner join categories b on b.id = a1.category_id
inner join category_relationships b1 on b1.parent_id = b.id
inner join categories c on c.id = b1.category_id
inner join category_relationships c1 on c1.parent_id = c.id
inner join categories d on d.id = c1.category_id
where a.parent = 1
union all
select concat(1000000 + a.id, '|',
1000000 + IFNULL(b.id,0), '|',
1000000 + IFNULL(c.id,0), '|',
1000000 + IFNULL(d.id,0), '|',
1000000 + IFNULL(e.id,0))
,concat(' - ', e.name)
from categories a
inner join category_relationships a1 on a1.parent_id = a.id
inner join categories b on b.id = a1.category_id
inner join category_relationships b1 on b1.parent_id = b.id
inner join categories c on c.id = b1.category_id
inner join category_relationships c1 on c1.parent_id = c.id
inner join categories d on d.id = c1.category_id
inner join category_relationships d1 on d1.parent_id = d.id
inner join categories e on e.id = d1.category_id
order by SORT

How can I get the exact match join for the below scenario?

How can i join the below tables
TableA TableB TableC TableD
ID ID_C ID ID_A Value ID ID ID_C Value
1 1 1 1 a 1 1 1 a
2 1 b 2 1 b
in order to get the Result like
Result
ID ID_B Value ID_C ID_D Value
1 1 a 1 1 a
1 2 b 1 2 b
and my result shouldn't contain 1 2 b 1 1 b and both value columns cannot always have same values so it cannot be used in a condition.
To make it simplier,
Resultant Table TableA TableB
ID Value ID Value ID ID_A
1 a 1 a 1 1
1 b 2 g 2 1
2 a 3 d 3 2
3 c 4 3
Now i need to join the Resultant Table with TableA,TableB inorder to get some of the columns from TableA,TableB and ResultantTable.ID=TableA.ID and TableB.ID_A=TableA.ID since its a foreign key.
Doing the Join with TableB turns to duplicates. Since ID=1 occurs twice i get 4 records where ID=1, when there are only 2 records. It can be done with distinct or group by but i need other columns as well to be displayed.How do i do both in the process.
SELECT A.ID, B.ID, B.Value, C.ID, D.ID, D.Value
FROM TableA A
INNER JOIN TableB B ON A.ID = B.ID_A
INNER JOIN TableC C ON A.ID_C = C.ID
INNER JOIN TableD D ON B.ID = D.ID AND C.ID = D.ID_C
You tell us that the field "value" in TableB should not be different from the field "value" in TableD? Could we replace the B.ID = D.ID with B.Value = D.Value so solve your problem?
Are you sure, that is the way that is suppose to work?
Try:
SELECT A.ID, B.ID ID_B, B.Value Value_B, C.ID ID_C, D.ID ID_D, D.Value Value_D
FROM TableA A
JOIN TableB B ON A.ID = B.ID_A
JOIN TableC C ON A.ID_C = C.ID
JOIN TableD D ON B.Value = D.Value AND C.ID = D.ID_C