I'm attempting to get a Count of how many times each book occurs in the SurveyDatas table for a each grade and each survey year.
In the query below the result in the Grade3 column for BookId 300 should actually be 1, but instead its 116. And same for the Grade4 column too. If I remove the Grade4 Count and JOIN, I get 58 which is half of 116, but still incorrect. I'm suspicious I need to use a Subquery instead of Left Joins for what I'm trying to do here, or perhaps there's even a more efficient way of doing this. Would SQL Server Common Table Expressions help me out here? I've never used that feature.
SELECT sd.SurveyYear, sd.BookId,
Count(sd3.Grade) as Grade3, Count(sd4.Grade) as Grade4
FROM SurveyDatas sd
LEFT JOIN SurveyDatas sd3 on sd3.BookId = sd.BookId
AND sd3.SurveyYear = sd.SurveyYear
AND sd3.Grade = '3'
LEFT JOIN SurveyDatas sd4 on sd4.BookId = sd.BookId
AND sd4.SurveyYear = sd.SurveyYear
AND sd4.Grade = '4'
GROUP BY sd.SurveyYear, sd.BookId
And here's my table structure and what my data looks like, although I do have more data entered than what I'm showing here.
SurveyDataId | SurveyYear | BookId | Grade
1 2014 300 3
2 2014 300 4
You are getting a cartesian product between the two. Instead, just use conditional aggregation:
SELECT sd.SurveyYear, sd.BookId,
sum(case when sd.Grade = '3' then 1 else 0 end) as Grade3,
sum(case when sd.Grade = '4' then 1 else 0 end) as Grade4
FROM SurveyDatas sd
GROUP BY sd.SurveyYear, sd.BookId;
The self-join is not necessary.
When you self join, you need to consider all columns. You aren't using the SurverDataID, and not getting the full picture because of it. Include it in you join condition, and you'll see what you expect.
SELECT sd.SurveyYear, sd.BookId,
Count(sd3.Grade) as Grade3, Count(sd4.Grade) as Grade4
FROM SurveyDatas sd
LEFT JOIN SurveyDatas sd3 on sd3.BookId = sd.BookId
AND sd3.SurveyYear = sd.SurveyYear
AND sd3.Grade = '3'
AND sd.SurveyDataID = sd3.SurveyDataID -- Add this line
LEFT JOIN SurveyDatas sd4 on sd4.BookId = sd.BookId
AND sd4.SurveyYear = sd.SurveyYear
AND sd4.Grade = '4'
AND sd.SurveyDataID = sd4.SurveyDataID -- And also this line
GROUP BY sd.SurveyYear, sd.BookId
When I was trouble shooting it, I removed the group by so that I could see all the rows. I should have seen nulls for Grade 4 where the survey was for Grade 3, and that tipped me off to the real cause.
Related
This question already has answers here:
Two SQL LEFT JOINS produce incorrect result
(3 answers)
Closed 9 months ago.
I may have messed up the joins or there may be another way of writing what I am trying to achieve.
My current query is this:
SELECT
i.SKI_NAME AS Description,
l.SKV_QUANTITY_IN_STOCK AS Qty,
SUM(CASE WHEN ad.AVN_STATUS = 'D' THEN li.AVL_QUANTITY ELSE '0' END) AS AdviceQty,
SUM(CASE WHEN con.HCT_STATUS = 'D' THEN item.HIT_QUANTITY ELSE '0' END) AS HireQty
FROM
TH_STOCK_LEVELS l
LEFT JOIN TH_STOCK_ITEMS i ON l.SKV_STOCK_NUMBER = i.SKI_STOCK_NUMBER
LEFT JOIN TH_ADVICE_NOTE_LINES li ON i.SKI_NAME = li.AVL_DESCRIPTION
LEFT JOIN TH_HIRE_ITEMS Item ON li.AVL_DESCRIPTION = Item.HIT_DESCRIPTION
LEFT JOIN TH_ADVICE_NOTES ad ON ad.AVN_ID = li.AVL_NOTE_NUMBER
LEFT JOIN TH_HIRE_CONTRACTS con ON con.HCT_CONTRACT_NUMBER = Item.HIT_CONTRACT_NUMBER
WHERE
l.SKV_DEPOT_ID = 7
GROUP BY i.SKI_NAME, l.SKV_QUANTITY_IN_STOCK;
This displays the following output:
Description
Qty
AdviceQty
HireQty
Some Item
2
400
100
Some Item
0
100
0
Which is incorrect, as it seems to be totalling all previous Advice's and Hire's and not just the ones with Status 'D'.
If I do the following to the query (comment some lines out):
SELECT
i.SKI_NAME AS Description,
l.SKV_QUANTITY_IN_STOCK AS Qty,
SUM(CASE WHEN ad.AVN_STATUS = 'D' THEN li.AVL_QUANTITY ELSE '0' END) AS AdviceQty
--SUM(CASE WHEN con.HCT_STATUS = 'D' THEN item.HIT_QUANTITY ELSE '0' END) AS HireQty
FROM
TH_STOCK_LEVELS l
LEFT JOIN TH_STOCK_ITEMS i ON l.SKV_STOCK_NUMBER = i.SKI_STOCK_NUMBER
LEFT JOIN TH_ADVICE_NOTE_LINES li ON i.SKI_NAME = li.AVL_DESCRIPTION
--LEFT JOIN TH_HIRE_ITEMS Item ON li.AVL_DESCRIPTION = Item.HIT_DESCRIPTION
LEFT JOIN TH_ADVICE_NOTES ad ON ad.AVN_ID = li.AVL_NOTE_NUMBER
--LEFT JOIN TH_HIRE_CONTRACTS con ON con.HCT_CONTRACT_NUMBER = Item.HIT_CONTRACT_NUMBER
WHERE
l.SKV_DEPOT_ID = 7
GROUP BY i.SKI_NAME, l.SKV_QUANTITY_IN_STOCK;
The output is correct, although I am now missing a column due to the comments. This also works if I comment out the Advice tables, the HireQty column would be correct.
Description
Qty
AdviceQty
Some Item
2
10
Some Item
0
0
How do I get this to display the correct data for both AdviceQty & HireQty without having to do them separately?
Sometimes, CASE statement doesn't work. You can try with IFs and see if it is working.
SELECT
i.SKI_NAME AS Description,
l.SKV_QUANTITY_IN_STOCK AS Qty, SUM(IF(ad.AVN_STATUS='D',li.AVL_QUANTITY,0)) AS AdviceQty, SUM(IF(con.HCT_STATUS='D',item.HIT_QUANTITY,0)) AS HireQty
FROM
TH_STOCK_LEVELS l
LEFT JOIN TH_STOCK_ITEMS i ON l.SKV_STOCK_NUMBER = i.SKI_STOCK_NUMBER LEFT JOIN TH_ADVICE_NOTE_LINES li ON i.SKI_NAME = li.AVL_DESCRIPTION LEFT JOIN TH_HIRE_ITEMS Item ON li.AVL_DESCRIPTION
= Item.HIT_DESCRIPTION LEFT JOIN TH_ADVICE_NOTES ad ON ad.AVN_ID = li.AVL_NOTE_NUMBER LEFT JOIN TH_HIRE_CONTRACTS con ON con.HCT_CONTRACT_NUMBER = Item.HIT_CONTRACT_NUMBER
WHERE
l.SKV_DEPOT_ID = 7
GROUP BY i.SKI_NAME, l.SKV_QUANTITY_IN_STOCK;
Elon Reeve Musk FRS is an entrepreneur and business magnate. He is the founder, CEO, and Chief Engineer at SpaceX; early-stage investor, CEO, and Product Architect of Tesla, Inc.; founder of The Boring Company; and co-founder of Neuralink and OpenAI.
Your inner select returns a table. That can't be used as parameter to match a WHERE IN condition. Instead try using an INNER JOIN
sum(decode(
select sum(dou.noukn)
from dou
join v_kzeiritsu on
dou.zeiritsu = v_kzeiritsu.zeiritsu
)) as noukn2;
Just move your sum logic inside select as follows:
(SELECT SUM(DOU$2.NOUKN)
FROM SDNISHI.V_KZEIRITSU V
WHERE DOU$2.ZEIRITSU = V.ZEIRITSU) AS NOUKN2
In case If it gives aggregation error then use sum(above query) AS NOUKN2
Your code is very strange. For instance, it seems to assume that V_KZEIRITSU has one row. But, you can move this to the FROM clause:
SELECT SUM(CASE WHEN DOU.ZEIRITSU = K.ZEIRITSU THEN DOU.NOUKN ELSE 0 END) AS NOUKN2
FROM DOU LEFT JOIN
V_KZEIRITSU K
ON 1=1 -- in case the table is really empty
A slightly more reasonable version would be:
SELECT SUM(DOU.NOUKN) AS NOUKN2
FROM DOU LEFT JOIN
V_KZEIRITSU K
ON DOU.ZEIRITSU = K.ZEIRITSU -- in case the table is really empty
It seems rather unlikely to me that this is what you really intend. If not, I would suggest that you ask a new question with appropriate same data, desired results, and explanation of the results. A non-working query should not be expected to provide the same level of information.
I'd say that it is, actually, as simple as
select sum(dou.noukn)
from dou
where dou.zeiritsu in (select zeiritsu from v_kzeiritsu)
(I'm not sure what dou is (table? alias?), but I hope you do.)
After you edited the question, I'm editing the answer. I marked with "--> this" two lines that - in my opinion - might help. As previously, the whole sum(case ...) as noukn2 is replaced by a simple sum(dou$2.noukn).
Note that - in Oracle - you can't use as keyword for table alias. For example:
no: from employees as e
yes: from employees e
Here's your query:
SELECT DOU$2.CUSTCD AS CUSTCD,
DOU$2.CHUNO AS CHUNO,
DOU$2.LINNO AS LINNO,
DOU$2.SHIPDAYYM AS SHIPDAYYM,
SUM (DOU$2.NOUKN) AS NOUKN,
SUM (DOU$2.ZEIKN) AS ZEIKN,
SUM (dou$2.noukn) AS noukn2 --> this
FROM SDNISHI.T_HCHUMON_DOUSOU DOU$2
INNER JOIN SDNISHI.SY_KANRI KNR ON KNR.SHIPDAYYM = DOU$2.SHIPDAYYM
INNER JOIN SDNISHI.T_HCHUMON_MEI MEI
ON MEI.CUSTCD = DOU$2.CUSTCD
AND MEI.CHUNO = DOU$2.CHUNO
AND MEI.LINNO = DOU$2.LINNO
AND MEI.SHIPDAYYM = DOU$2.SHIPDAYYM
AND MEI.USEDNGKBN = '0'
AND MEI.CANCELKBN = '0'
LEFT OUTER JOIN SDNISHI.T_HCHUMON_HD HD
ON HD.CUSTCD = MEI.CUSTCD
AND HD.CHUNO = MEI.CHUNO
AND HD.LINNO = MEI.LINNO
AND HD.USEDNGKBN = '0'
AND HD.CANCELKBN = '0'
AND isnull (HD.CANKBN, '00') = '00'
JOIN v_keziritsu vk ON vk.zeiritsu = dou$2.zeiritsu --> this
WHERE DOU$2.USEDNGKBN = '0'
AND DOU$2.CANCELKBN = '0'
AND ( ( MEI.CHGDELKBN = '1'
AND MEI.HDOUSOUKBN = '02'
AND ( MEI.CHUSU > 0
OR MEI.BCHUSU > 0))
OR ( MEI.CHGDELKBN != '1'
AND HD.HDOUSOUKBN = '02'
AND ( MEI.CHKBTNFGA = '1'
AND HD.CHUSU > 0)
OR ( MEI.CHKBTNFGB = '1'
AND HD.BCHUSU > 0)))
GROUP BY DOU$2.CUSTCD,
DOU$2.CHUNO,
DOU$2.LINNO,
DOU$2.SHIPDAYYM
SELECT
AEC.gwd_people.id_people,
AEC.gwd_people.uid_people,
AEC.gwd_people.cod_people,
AEC.gwd_people.name_people,
AEC.gwd_people.surname_people,
AEC.gwd_people.email,
AEC.gwd_people.people_status,
AEC.gwd_people.people_type,
AEC.gwd_people.facility_reference,
AEC.gwd_people.sc_id_sap,
AEC.gwd_people.c_id_sap,
AEC.gwd_people.descr_people,
AEC.gwd_people.cod_sector,
AEC.gwd_people.descr_sector,
AEC.gwd_people.cod_org_sector,
AEC.gwd_people.descr_org_sector,
AEC.gwd_people.cod_company,
AEC.gwd_people.descr_company,
AEC.gwd_people.cod_company_sap,
AEC.gwd_people.cod_department,
AEC.gwd_department.descr_department,
AEC.gwd_people.cod_subdepartment,
AEC.gwd_people.descr_subdepartment,
AEC.gwd_people.cod_cdc,
AEC.gwd_cost_center.descr_cdc,
AEC.gwd_people.cod_category_job,
AEC.gwd_people.descr_category_job,
AEC.gwd_people.cod_people_job,
AEC.gwd_people.descr_people_job,
AEC.gwd_people.cod_position,
AEC.gwd_people.descr_position,
AEC.gwd_people.uohr,
AEC.gwd_people.qual_contract,
AEC.gwd_people.level_position,
AEC.gwd_people.cod_manager,
AEC.gwd_people.cod_validator,
AEC.gwd_people.cod_country,
AEC.gwd_people.descr_country,
AEC.gwd_people.cod_region_area,
AEC.gwd_people.descr_region_area,
AEC.gwd_people.descr_city,
AEC.gwd_people.descr_site,
AEC.gwd_people.address_1,
AEC.gwd_people.address_2,
AEC.gwd_people.descr_building,
AEC.gwd_people.descr_room,
AEC.gwd_people.validity_date,
AEC.aec_workstation.cod_workstation,
AEC.aec_workstation.geometry,
AEC.aec_workstation.drawing,
AEC.gwd_people.tax_code,
AEC.gwd_people.phone_1,
AEC.gwd_people.phone_2,
AEC.gwd_people.phone_3,
AEC.gwd_people.phone_4,
AEC.gwd_people.ext_email_1,
AEC.gwd_people.flagvip,
AEC.gwd_people.hiring_date,
AEC.gwd_people.cease_date,
AEC.gwd_people.cid_resp_liv_1,
AEC.gwd_people.cid_resp_liv_2,
AEC.gwd_people.id_resp,
AEC.gwd_people.descr_resp,
AEC.gwd_people.id_ref,
AEC.gwd_people.descr_ref,
AEC.gwd_people.descr_ext_people,
AEC.gwd_people.ext_email_2,
AEC.gwd_people.descr_sede,
(CASE WHEN AEC.aec_r_workstation_people.cod_people IS NULL
THEN AEC.gwd_people.idplan
ELSE NULL
END) AS idplan,
(CASE WHEN AEC.aec_r_workstation_people.cod_people IS NOT NULL
THEN SUBSTRING(AEC.aec_workstation.cod_workstation, 5, 7)
ELSE NULL
END) AS idplan_wrkst,
(CASE WHEN AEC.aec_r_workstation_people.cod_people IS NULL
THEN AEC.view_iam_r_unitp_building.IDEDIFICIO
ELSE NULL
END) AS cod_building,
(CASE WHEN AEC.aec_r_workstation_people.cod_people IS NOT NULL
THEN SUBSTRING(AEC.aec_workstation.cod_workstation, 5, 3)
ELSE NULL
END) AS cod_building_wrkst,
(CASE WHEN AEC.aec_r_workstation_people.cod_people IS NOT NULL
THEN AEC.aec_workstation.id_room
ELSE NULL
END) AS id_room_wrkst,
(CASE WHEN AEC.aec_r_workstation_people.cod_people IS NOT NULL
THEN AEC.aec_workstation.id_room
ELSE NULL
END) AS id_room_wrkst2
FROM AEC.gwd_people
LEFT OUTER JOIN AEC.view_iam_r_unitp_building ON
AEC.view_iam_r_unitp_building.IDUNITPROD = AEC.gwd_people.cod_sector
LEFT OUTER JOIN AEC.aec_r_workstation_people ON AEC.gwd_people.cod_people =
AEC.aec_r_workstation_people.cod_people
LEFT OUTER JOIN AEC.aec_workstation ON AEC.aec_workstation.cod_workstation
= AEC.aec_r_workstation_people.cod_workstation
LEFT OUTER JOIN AEC.gwd_department ON AEC.gwd_department.cod_department =
AEC.gwd_people.cod_department
LEFT OUTER JOIN AEC.gwd_cost_center ON AEC.gwd_cost_center.cod_cost_center
= AEC.gwd_people.cod_cdc
This is my query and I'm using SQL Server 13, it returns 6752 rows, 44 of them are duped. I've tried everything I know to avoid showing those duped entries but I'm out of ideas, so I'm looking for some helpful tips :-) One of the biggest problem is taht all fields are necessary, so I can't get rid of "AEC.aec_workstation.geometry" that causes problems with SELECT DISTINCT.
Find a PK value from your first table that's returning a duplicate row and start with the following query:
SELECT
COUNT(1)
FROM
AEC.gwd_people
WHERE
AEC.gwd_people.PrimaryKeyColumn = 'SomeValue'
Now start adding joins one by one, checking the result of the COUNT(1) each time:
SELECT
COUNT(1)
FROM
AEC.gwd_people
LEFT OUTER JOIN AEC.view_iam_r_unitp_building ON AEC.view_iam_r_unitp_building.IDUNITPROD = AEC.gwd_people.cod_sector
WHERE
AEC.gwd_people.PrimaryKeyColumn = 'SomeValue'
And then...
SELECT
COUNT(1)
FROM
AEC.gwd_people
LEFT OUTER JOIN AEC.view_iam_r_unitp_building ON AEC.view_iam_r_unitp_building.IDUNITPROD = AEC.gwd_people.cod_sector
LEFT OUTER JOIN AEC.aec_r_workstation_people ON AEC.gwd_people.cod_people = AEC.aec_r_workstation_people.cod_people
WHERE
AEC.gwd_people.PrimaryKeyColumn = 'SomeValue'
Until you see the amount of rows jump up when you don't expect it to. You are most likely:
Not considering that duplicate rows can be expected.
Missing another join column on a table.
Having duplicate rows on a table.
... or combination of these.
Your table design makes it a bit hard to understand their relations. This is what it looks like to me:
gwd_department {1:n} gwd_people
gwd_people {m:n} aec_workstation
gwd_people {m:n} view_iam_r_unitp_building
gwd_people {?:n} gwd_cost_center
So for a person linked to 3 aec_workstations and 4 view_iam_r_unitp_buildings, you'd produce 3 x 4 = 12 result rows. Is there no further relation between an aec_workstation and a view_iam_r_unitp_building? If not, then why do you combine them in your query?
I don't know whether cod_cdc is supposed to be short for cod_cost_center or something different. If this is an m:n relation, too, you are doing the same thing again with gwd_cost_center related to aec_workstation and view_iam_r_unitp_building.
Having said this: Either add the missing criteria or ask yourself what you want to select after all.
I'm trying to get a count of all speakers who are active regarding that item as well as the total of speakers who correlate to a certain item. The first LEFT JOIN for the total speakers works, but the other for ONLY the active speakers regarding that item doesn't, any help is appreciated. The SQLFiddle is here
http://sqlfiddle.com/#!3/b579d/1
But when I try to add in the portion where you would get the number of active speakers
(LEFT JOIN (SELECT COUNT (tbl_SpeakerCard_Log.SpeakerName)
WHERE tbl_Speaker_Log.Spoken = 0)
ON tbl_AgendaList.AID = tbl_SpeakerCard_Log.AID)
under the previous LEFT JOIN I get an error. I'm 100% sure the query is wrong in some form, but I'm not sure how to approach it.
*NOTE: Spoken/Active are interchangeable, I just use different wording to clarify what I'm looking for.
EDIT: This is the desired output
http://imgur.com/yP1FKxg
You can use conditional aggregation to do this:
SELECT
AgendaList.AID,
AgendaList.Item,
COUNT(SpeakerList.SPID) as SpeakerTotal,
SUM(CASE WHEN SpeakerList.Spoken = 0 THEN 1 ELSE 0 END) as ActiveSpeakers
FROM AgendaList
LEFT JOIN SpeakerLIST ON AgendaList.AID = SpeakerList.AID
GROUP BY AgendaList.AID, AgendaList.Item;
Sample SQL Fiddle
Or you could use count instead of sum (which might be clearer):
COUNT(CASE WHEN Spoken = 0 THEN Spoken END) as ActiveSpeakers
SQL FIDDLE
WITH sTotal AS (
SELECT AgendaList.AID, AgendaList.Item, COUNT( SpeakerList.SPID) as SpeakerTotal
FROM AgendaList
LEFT JOIN SpeakerLIST ON AgendaList.AID = SpeakerList.AID
GROUP BY AgendaList.AID, AgendaList.Item
),
sActive AS (
SELECT AgendaList.AID, AgendaList.Item, COUNT( SpeakerList.SPID) as SpeakerActive
FROM AgendaList
LEFT JOIN SpeakerLIST ON AgendaList.AID = SpeakerList.AID
WHERE SpeakerLIST.Spoken = 0
GROUP BY AgendaList.AID, AgendaList.Item
)
SELECT sTotal.*, sActive.SpeakerActive
FROM sTotal left join
sActive on sTotal.AID = sActive.AID
i have a table campaign which has details of campaign mails sent.
campaign_table: campaign_id campaign_name flag
1 test1 1
2 test2 1
3 test3 0
another table campaign activity which has details of campaign activities.
campaign_activity: campaign_id is_clicked is_opened
1 0 1
1 1 0
2 0 1
2 1 0
I want to get all campaigns with flag value 3 and the number of is_clicked columns with value 1 and number of columns with is_opened value 1 in a single query.
ie. campaign_id campaign_name numberofclicks numberofopens
1 test1 1 1
2 test2 1 1
I did this using sub-query with the query:
select c.campaign_id,c.campaign_name,
(SELECT count(campaign_id) from campaign_activity WHERE campaign_id=c.id AND is_clicked=1) as numberofclicks,
(SELECT count(campaign_id) from campaign_activity WHERE campaign_id=c.id AND is_clicked=1) as numberofopens
FROM
campaign c
WHERE c.flag=1
But people say that using sub-queries are not a good coding convention and you have to use join instead of sub-queries. But i don't know how to get the same result using join. I consulted with some of my colleagues and they are saying that its not possible to use join in this situation. Is it possible to get the same result using joins? if yes, please tell me how.
This should do the trick. Substitute INNER JOIN for LEFT OUTER JOIN if you want to include campaigns which have no activity.
SELECT
c.Campaign_ID
, c.Campaign_Name
, SUM(CASE WHEN a.Is_Clicked = 1 THEN 1 ELSE 0 END) AS NumberOfClicks
, SUM(CASE WHEN a.Is_Opened = 1 THEN 1 ELSE 0 END) AS NumberOfOpens
FROM
dbo.Campaign c
INNER JOIN
dbo.Campaign_Activity a
ON a.Campaign_ID = c.Campaign_ID
GROUP BY
c.Campaign_ID
, c.Campaign_Name
Assuming is_clicked and is_opened are only ever 1 or 0, this should work:
select c.campaign_id, c.campaign_name, sum(d.is_clicked), sum(d.is_opened)
from campaign c inner join campaign_activity d
on c.campaign_id = d.campaign_id
where c.flag = 1
group by c.campaign_id, c.campaign_name
No sub-queries.
Hmm. Is what you want as simple as this? I'm not sure I'm reading the question right...
SELECT
campaign_table.campaign_id, SUM(is_clicked), SUM(is_opened)
FROM
campaign_table
INNER JOIN campaign_activity ON campaign_table.campaign_id = campaign_activity.campaign_id
WHERE
campaign_table.flag = 1
GROUP BY
campaign_table.campaign_id
Note that with an INNER JOIN here, you won't see campaigns where there's nothing corresponding in the campaign_activity table. In that circumstance, you should use a LEFT JOIN, and convert NULL to 0 in the SUM, e.g. SUM(IFNULL(is_clicked, 0)).
I suppose this should do it :
select * from campaign_table inner join campaign_activity on campaign_table.id = campaign_activity.id where campaign_table.flag = 3 and campaign_activity.is_clicked = 1 and campaign_activity.is_opened = 1
Attn : this is not tested in a live situation
The SQL in it's simplest form and most robust form is this: (formatted for readability)
SELECT
campaign_table.campaign_ID, campaign_table.campaign_name, Sum(campaign_activity.is_clicked) AS numberofclicks, Sum(campaign_activity.is_open) AS numberofopens
FROM
campaign_table INNER JOIN campaign_activity ON campaign_table.campaign_ID = campaign_activity.campaign_ID
GROUP BY
campaign_table.campaign_ID, campaign_table.campaign_name, campaign_table.flag
HAVING
campaign_table.flag=1;