Finding value when multiple rows by group by - sql

Lets say we have the following data sets
tbl_building:
id -- name
1 -- building 1
2 -- building 2
tbl_rooms:
id -- building_id -- room_id -- light_status
1 ------ 1 ------------- 1 ----------- 0
2 ------ 1 ------------- 2 ----------- 1
3 ------ 1 ------------- 3 ----------- 0
4 ------ 2 ------------- 1 ----------- 1
How would I construct a single sql statement to find out which BUILDINGS have a light switched on in a YES/NO format Whilst grouping by Building name
Idealling I want something like the following:
SELECT b.name, if(light_status, 'yes', no) as light_status
FROM tbl_building b
JOIN tbl_rooms r on b.id = r.building_id
group by b.id
However, this seems to be random as to which room it will bring back for each buildinh

Select b.name, case when sum (a.light_status) > 0 then 'YES' else 'NO' end as LightStatus
From tbl_rooms a
Join tbl_buildings b
On a.building_id = b.building_id
Group by b.name

A simple case for a semi-join:
SELECT name
FROM tbl_building b
WHERE EXISTS (
SELECT 1
FROM tbl_rooms r
WHERE b.id = r.building_id
AND light_status = 1
)
This will return those buildings where at least one room has their light switched on.

select (case when tbl_rooms.light_status = 1 then building_id end) as building_id_on,
(case when tbl_rooms.light_status = 0 then building_id end) as building_id_on
from tbl_building inner join tbl_rooms on tbl_building.id = tbl_rooms.building_id

Try this:
SELECT DISTINCT B.name
FROM tbl_rooms A
INNER JOIN tbl_builiding B
ON A.building_id = B.id

To find buildings with at least one room with the lights turned on:
SELECT
B.name
FROM tbl_rooms A
INNER JOIN tbl_builiding B
ON A.building_id = B.id
GROUP BY B.name
HAVING MAX(light_status) = 1
To list all buildings and wether or not they have at least one room with the light turned on:
SELECT
B.name, IIF(MAX(light_status) = 1, 'YES', 'NO') as light_status
FROM tbl_rooms A
INNER JOIN tbl_builiding B
ON A.building_id = B.id
GROUP BY B.name

Related

SQL JOIN gives me 0 entry if stock has no product relationship [duplicate]

This question already has answers here:
MySQL SELECT JOIN with empty row returns
(2 answers)
Closed 1 year ago.
SQL QUERY:
SELECT
c.id AS c_id,c.image AS category_image,b.id AS p_id,b.image AS product_image,
a.id as product_id,0 AS sc_id,0 AS sub_category,0 AS is_active2, a.*,b.*,c.*
FROM stock_50644 a
JOIN product b ON a.product_id = b.id
JOIN category c ON b.category = c.id
WHERE b.category=1 ORDER BY p_id ASC
If stock_50644 has no product entry then i am getting empty result. How can i make it return all product with stock value null if stock is empty
EDIT:
PRODUCT ; STOCK_50644
id product_name category_id ; id product_id price
---- -------- ---------- --------------- ; ---- --------- ----- --------------
1 name1 1 ; 1 2 15
2 name2 2 ;
if i put WHERE b.id=1 in below query im getting correct expected output.
But as soon as i replace it by ORDER BY b.id ASC LIMIT 1; it is taking forever time and then #2013 - Lost connection to MySQL server during query
SELECT
c.id AS c_id, c.image AS category_image, b.id AS p_id, b.image AS product_image,
a.id AS product_id, 0 AS sc_id, 0 AS sub_category, 0 AS is_active2, a.*,b.*,c.*
FROM stock_50644 a
RIGHT JOIN product b ON a.product_id = b.id AND b.category = 1
LEFT JOIN category c ON b.category = c.id
WHERE b.id=1
ORDER BY b.id ASC LIMIT 1;
Move the restriction on the category from the WHERE clause to the ON clause of the join:
SELECT
c.id AS c_id, c.image AS category_image, b.id AS p_id, b.image AS product_image,
a.id AS product_id, 0 AS sc_id, 0 AS sub_category, 0 AS is_active2,
a.*,b.*,c.*
FROM product b
LEFT JOIN stock_50644 a
ON a.product_id = b.id AND b.category = 1
LEFT JOIN category c
ON b.category = c.id
ORDER BY p_id;

How to show all same values from a column in sql?

I have a table Group
group no -of -win
------ ---
a 3
b 3
c 4
How can I show
group
---------
a
b
as output? That is, the set of groups that have the same number of wins.
use self join
DEMO
select distinct a.group from tablename a
join tablename b where a.group<>b.group and a.noofwin=b.noofwin
OUTPUT:
grp
a
b
SELECT GroupName
FROM Groups a
WHERE EXISTS(SELECT TOP 1 1 FROM Groups b WHERE b.GroupName <> a.GroupName AND b.Score = a.Score)

Optimizing SQL query having DISTINCT keyword and functions

I have this query that generates about 40,000 records and the execution time of this query is about 1 minute 30 seconds.
SELECT DISTINCT
a.ID,
a.NAME,
a.DIV,
a.UID,
(select NAME from EMPLOYEE where UID= a.UID and UID<>'') as boss_id,
(select DATE(MAX(create_time)) from XYZ where XYZ_ID= 1 and id = a.ID) as TERM1,
(select DATE(MAX(create_time)) from XYZ where XYZ_ID= 2 and id = a.ID) as TERM2,
(select DATE(MAX(create_time)) from XYZ where XYZ_ID= 3 and id = a.ID) as TERM3,
(select DATE(MAX(create_time)) from XYZ where XYZ_ID= 4 and id = a.ID) as TERM4,
(select DATE(MAX(create_time)) from XYZ where XYZ_ID= 5 and id = a.ID) as TERM5,
(select DATE(MAX(create_time)) from XYZ where XYZ_ID= 6 and id = a.ID) as TERM6,
(select DATE(MAX(create_time)) from XYZ where XYZ_ID= 7 and id = a.ID) as TERM7,
(select DATE(MAX(create_time)) from XYZ where XYZ_ID= 8 and id = a.ID) as TERM8
FROM EMPLOYEE a
WHERE ID LIKE 'D%'
I tried using group by, different kinds of join to improve the execution time but couldn't succeed.Both the tables ABC and XYZ are indexed.
Also, I think that the root cause of this problem is either the DISTINCT keyword or the MAX function.
How can I optimize the above query to bring down the execution time to at least less than a minute?
Any help is appreciated.
Query is not tested, this is just an idea on how you could get this done in two different ways.
(SQL Server solutions here)
Using LEFT JOIN for each ID should look something like this:
SELECT a.ID,
a.NAME,
a.DIV,
a.UID,
b.Name as boss_id,
MAX(xyz1.create_time) as TERM1,
MAX(xyz2.create_time) as TERM2,
MAX(xyz3.create_time) as TERM3,
MAX(xyz4.create_time) as TERM4,
MAX(xyz5.create_time) as TERM5,
MAX(xyz6.create_time) as TERM6,
MAX(xyz7.create_time) as TERM7,
MAX(xyz8.create_time) as TERM8
FROM EMPLOYEE a
JOIN EMPLOYEE b on a.UID = b.UID and b.UID <> ''
LEFT JOIN XYZ xyz1 on a.ID = xyz1.ID and xyz1.XYZ_ID = 1
LEFT JOIN XYZ xyz2 on a.ID = xyz2.ID and xyz1.XYZ_ID = 2
LEFT JOIN XYZ xyz3 on a.ID = xyz3.ID and xyz1.XYZ_ID = 3
LEFT JOIN XYZ xyz4 on a.ID = xyz4.ID and xyz1.XYZ_ID = 4
LEFT JOIN XYZ xyz5 on a.ID = xyz5.ID and xyz1.XYZ_ID = 5
LEFT JOIN XYZ xyz6 on a.ID = xyz6.ID and xyz1.XYZ_ID = 6
LEFT JOIN XYZ xyz7 on a.ID = xyz7.ID and xyz1.XYZ_ID = 7
LEFT JOIN XYZ xyz8 on a.ID = xyz8.ID and xyz1.XYZ_ID = 8
WHERE a.ID LIKE 'D%'
GROUP BY a.ID, a.NAME, a.DIV, a.UID, b.Name
Using PIVOT would look something like this:
select * from (
SELECT DISTINCT
a.ID,
a.NAME,
a.DIV,
a.UID,
b.NAME as boss_id,
xyz.xyz_id,
xyz.create_time
FROM EMPLOYEE a
JOIN EMPLOYEE b on a.UID = b.UID and b.UID <> ''
LEFT JOIN (SELECT DATE(MAX(create_time)) create_time, XYZ_ID, ID
from XYZ
where XYZ_ID between 1 and 8
group by XYZ_ID, ID) xyz on a.ID = xyz1.ID
WHERE a.ID LIKE 'D%') src
PIVOT (
max(create_time) for xyz_id IN (['1'], ['2'], ['3'], ['4'],
['5'], ['6'], ['7'], ['8'])
) PIV
Give it a shot
I would recommend group by and conditional aggregation:
SELECT e.ID, e.NAME, e.DIV, e.UID,
DATE(MAX(CASE WHEN XYZ_ID = 1 THEN create_time END)) as term1,
DATE(MAX(CASE WHEN XYZ_ID = 2 THEN create_time END)) as term2,
DATE(MAX(CASE WHEN XYZ_ID = 3 THEN create_time END)) as term3,
DATE(MAX(CASE WHEN XYZ_ID = 4 THEN create_time END)) as term4,
DATE(MAX(CASE WHEN XYZ_ID = 5 THEN create_time END)) as term5,
DATE(MAX(CASE WHEN XYZ_ID = 6 THEN create_time END)) as term6,
DATE(MAX(CASE WHEN XYZ_ID = 7 THEN create_time END)) as term7,
DATE(MAX(CASE WHEN XYZ_ID = 8 THEN create_time END)) as term8
FROM EMPLOYEE e LEFT JOIN
XYZ
ON xyz.ID = e.id
WHERE e.ID LIKE 'D%'
GROUP BY e.ID, e.NAME, e.DIV, e.UID;
I don't understand the logic for boss_id, so I left that out. This should improve the performance significantly.

Different output when using count and group by

When trying to get a count of IDs I get a different answer when grouping by day vs when I am not.
select cv.CONV_DAY, count(distinct cv.CLICK_ID)
from
clickcache.click cc
right join(
select distinct cv.CLICK_ID, cv.CONV_DAY, cv.PIXEL_ID
from clickcache.CONVERSION cv
where cv.CLICK_ID IS NOT NULL) cv ON cv.CLICK_ID = cc.ID
where cc.ADV_ACCOUNT_ID = 25176
and cv.CONV_DAY between '2016-8-01' AND '2016-08-07'
and AMP_CLICK_STATUS_ID = 1
AND pixel_id IN
(SELECT DISTINCT conversion_pixel_id
FROM
ampx.campaign_event_funnel ef
JOIN ampx.campaign cp ON
cp.id = ef.campaign_id
AND cp.campaign_status_id = 1
WHERE
ef.account_id IN(25176)
AND include_optimization = 1 )
group by 1
order by 1 asc
This yields 170 which is the correct answer and the I want. This, on the other hand, displays 157.
select count(distinct cv.CLICK_ID)
from
clickcache.click cc
right join(
select distinct cv.CLICK_ID, cv.CONV_DAY, cv.PIXEL_ID
from clickcache.CONVERSION cv
where cv.CLICK_ID IS NOT NULL) cv ON cv.CLICK_ID = cc.ID
where cc.ADV_ACCOUNT_ID = 25176
and cv.CONV_DAY between '2016-8-01' AND '2016-08-07'
and AMP_CLICK_STATUS_ID = 1
AND pixel_id IN
(SELECT DISTINCT conversion_pixel_id
FROM
ampx.campaign_event_funnel ef
JOIN ampx.campaign cp ON
cp.id = ef.campaign_id
AND cp.campaign_status_id = 1
WHERE
ef.account_id IN(25176)
AND include_optimization = 1 )
My question is why do I get this discrepancy and how to fix it to get a proper count?
Thank you!
Your count dependents from right query, maybe you have duplicate row?
example
table1
id name value
1 2 3
table2
id name value
1 4 5
2 6 3
1 6 3
right join tables on value get result
select * from table1 a right join table2 b on a.value = b.value
1 2 3 2 6 3
1 2 3 1 6 3
select count(distinct a.value)
from (select a.id, a.name, a.value, b.id, b.name, b.value
from table1 a right join table2 b on a.value = b.value)
result is 1
select b.id, count(distinct a.value)
from (select a.id, a.name, a.value, b.id, b.name, b.value
from table1 a right join table2 b on a.value = b.value group)
group by b.id
result is two rows
2 1
1 1
My guess is that, you have a problem for this reason.

sql combine two subqueries

I have two tables. Table A has an id column. Table B has an Aid column and a type column. Example data:
A: id
--
1
2
B: Aid | type
----+-----
1 | 1
1 | 1
1 | 3
1 | 1
1 | 4
1 | 5
1 | 4
2 | 2
2 | 4
2 | 3
I want to get all the IDs from table A where there is a certain amount of type 1 and type 3 actions. My query looks like this:
SELECT id
FROM A
WHERE (SELECT COUNT(type)
FROM B
WHERE B.Aid = A.id
AND B.type = 1) = 3
AND (SELECT COUNT(type)
FROM B
WHERE B.Aid = A.id
AND B.type = 3) = 1
so on the data above, just the id 1 should be returned.
Can I combine the 2 subqueries somehow? The goal is to make the query run faster.
Does postgres support CTEs?
WITH counts (Counts, Type, Aid) as (
select count(type), type
from b group by Type, Aid
)
select id
from A
join Counts B1 on b1.Aid = a.id and b1.type = 1
join Counts B3 on b3.Aid = a.id and b3.type = 3
where
b1.counts = 3 and b3.counts = 1
I'd suggest comparing the execution plans, but I suspect it would be similar since everything should get collapsed before execution.
Select ...
From A
Join (
Select B.Id
, Sum ( Case When B.Type = 1 Then 1 Else 0 End ) As Type1Count
, Sum ( Case When B.Type = 3 Then 1 Else 0 End ) As Type3Count
From B
Where B.Type In(1,3)
Group By B.Id
) As Z
On Z.Id = A.Id
Where Z.Type1Count = 3
And Z.Type3Count = 1
This works in TSQL, does it work in Postgres?
SELECT A.ID
FROM A
WHERE A.ID in
(
SELECT AID
FROM B
GROUP BY AID
HAVING
SUM(CASE WHEN Type = 1 THEN 1 ELSE 0 END) = 3
OR SUM(CASE WHEN Type = 3 THEN 1 ELSE 0 END) = 1
)
Another alternative:
SELECT DISTINCT Aid FROM (
SELECT Aid,type,count(*) as n from B
GROUP BY Aid,type, ) as g
WHERE ( g.n=1 AND g.type = 3 )
OR ( g.n=3 AND g.type = 1 )
I doubt this will perform better than your original, though.
You seem to be doing the best strategy: counting only the candidate rows.
Perhaps some redundant prefiltering might help:
SELECT DISTINCT Aid FROM (
SELECT Aid,type,count(*) as n from B
WHERE g.type = 3 OR g.type = 1 -- prefilter
GROUP BY Aid,type, ) as g
WHERE ( g.n=1 AND g.type = 3 )
OR ( g.n=3 AND g.type = 1 )