Conditional statements within select for strings - sql

at the moment I query my database to find all interactions within a certain date parameter. I also join that table with my InteractionAttendees then join it by TargetGroups table. InteractionAttendees tells me who attended the interaction, TargetGroup tells me about the person, if they are part of a target group such as aboriginal or francophone.
I'm having trouble setting my Aboriginal and Francophone columns. I want to set it to be true if there was any attendees that are in the TargetGroup Table.
select CONVERT(date, getdate()) as ActivityDate,
CASE when Type = 0 Then 'General'
when Type = 1 Then 'Activity'
else 'Task' End as Type,
CASE when Indepth = 0 Then 'False' else 'True' End as Indepth,
Subject,
Comments,
'false' as Aboriginal,
'false' as FrancoPhone,
'false' as Female,
'false' as Youth,
'false' as Other
from Sarnia.dbo.Interactions as X
full outer join sarnia.dbo.InteractionAttendees as Y on X.Id = Y.Interaction_Id
full outer join Sarnia.dbo.TargetGroups as Z on Y.Person_Id = Z.PersonId
where ActivityDate >= '2015-07-01' and ActivityDate <= '2015-09-30'
group by ActivityDate, Type, Indepth, Created, subject, comments
for example
Interaction Table
Id
1
Interaction Attendee Table
Id InteractionId PersonId
1 1 5
2 1 10
TargetGroups Table
Id PersonId TargetValue
1 5 Aboriginal
2 10 Francophone
So my resulted table would be
Activity Date Aboriginal Francophone
---- True True
May I ask how do I properly populate my target group columns.

You can update your query something like this: Pivot in the old fashioned way. Based on your previous questions I am assuming it is a sql server DB.
MAX(IIF(TargetValue = 'Aboriginal', 'True', 'False')) as Aboriginal,
MAX(IIF(TargetValue = 'FrancoPhone', 'True', 'False')) as FrancoPhone
If you have SQL Server 2008 or older you can use something like this
MAX(CASE WHEN TargetValue = 'Aboriginal' THEN 'True' ELSE 'False' END) as Aboriginal,
MAX(CASE WHEN TargetValue = 'FrancoPhone' THEN 'True' ELSE 'False' END) as FrancoPhone

Here is an example of how to do this with a CTE. Note, I added this to the example sql you gave, but that sql has many errors.
with igroup as
(
SELECT IID,
CASE WHEN ACOUNT > 0 THEN true ELSE false END AS Aboriginal,
CASE WHEN FCOUNT > 0 THEN true ELSE false END AS Francophone
FROM (
SELECT InteractionId as IID,
SUM(CASE WHEN TargetValue = 'Aboriginal' THEN 1 ELSE 0 END) as ACOUNT,
SUM(CASE WHEN TargetValue = 'FrancoPhone' THEN 1 ELSE 0 END) as FCOUNT
FROM sarnia.dbo.InteractionAttendees A
LEFT JOIN sarnia.dbo.TargetGroups as G on A.Person_Id = G.PersonId
GROUP BY InteractionId
) X
)
select CONVERT(date, getdate()) as ActivityDate,
CASE when Type = 0 Then 'General'
when Type = 1 Then 'Activity'
else 'Task' End as Type,
CASE when Indepth = 0 Then 'False' else 'True' End as Indepth,
Subject,
Comments,
igroup.Aboriginal,
igroup.FrancoPhone,
from Sarnia.dbo.Interactions as X
full outer join sarnia.dbo.InteractionAttendees as Y on X.Id = Y.Interaction_Id
join igroup on X.Id = igroup.IID
where ActivityDate >= '2015-07-01' and ActivityDate <= '2015-09-30'
group by ActivityDate, Type, Indepth, Created, subject, comments

Related

HAVING gives me "column...does not exist" but I see the column

This is a practice question from stratascratch and I'm literally stuck at the final HAVING statement.
Problem statement:
Find the total number of downloads for paying and non-paying users by date. Include only records where non-paying customers have more downloads than paying customers. The output should be sorted by earliest date first and contain 3 columns date, non-paying downloads, paying downloads.
There are three tables:
ms_user_dimension (user_id, acc_id)
ms_acc_dimension (acc_id, paying_customer)
ms_download_facts (date, user_id, downloads)
This is my code so far
SELECT date,
SUM(CASE WHEN paying_customer = 'no' THEN cnt END) AS no,
SUM(CASE WHEN paying_customer = 'yes' THEN cnt END) AS yes
FROM (
SELECT date, paying_customer, SUM(downloads) AS cnt
FROM ms_download_facts d
LEFT JOIN ms_user_dimension u ON d.user_id = u.user_id
LEFT JOIN ms_acc_dimension a ON u.acc_id = a.acc_id
GROUP BY 1, 2
ORDER BY 1, 2
) prePivot
GROUP BY date
HAVING no > yes;
If I remove the HAVING no > yes at the end, the code will run and I can see I have three columns: date, yes, and no. However, if I add the HAVING statement, I get the error "column "no" does not exist...LINE 13: HAVING no > yes"
Can't figure out for the sake of my life what's going on here. Please let me know if anyone figures out something. TIA!
You don't need a subquery for this:
SELECT d.date,
SUM(CASE WHEN a.paying_customer = 'no' THEN d.downloads END) AS no,
SUM(CASE WHEN a.paying_customer = 'yes' THEN d.downloads END) AS yes
FROM ms_download_facts d LEFT JOIN
ms_user_dimension u
ON d.user_id = u.user_id LEFT JOIN
ms_acc_dimension a
ON u.acc_id = a.acc_id
GROUP BY d.date
HAVING SUM(CASE WHEN a.paying_customer = 'no' THEN d.downloads END) > SUM(CASE WHEN a.paying_customer = 'yes' THEN d.downloads END);
You can simplify the HAVING clause to:
HAVING SUM(CASE WHEN a.paying_customer = 'no' THEN 1 ELSE -1 END) > 0
This version assumes that paying_customer only takes on the values 'yes' and 'no'.
You may be able to simplify the query further, depending on the database you are using.
It doesn't like aliases in the having statement. Replace no with:
SUM(CASE WHEN paying_customer = 'no' THEN cnt END)
and do the similar thing for yes.
SELECT date,
SUM(CASE WHEN paying_customer = 'no' THEN cnt END) AS no,
SUM(CASE WHEN paying_customer = 'yes' THEN cnt END) AS yes
FROM (
SELECT date, paying_customer, SUM(downloads) AS cnt
FROM ms_download_facts d
LEFT JOIN ms_user_dimension u ON d.user_id = u.user_id
LEFT JOIN ms_acc_dimension a ON u.acc_id = a.acc_id
GROUP BY 1, 2
ORDER BY 1, 2
) prePivot
GROUP BY date
HAVING SUM(CASE WHEN paying_customer = 'no' THEN cnt END) > SUM(CASE WHEN paying_customer = 'yes' THEN cnt END);

Dynamicaly Naming Columns - Using SQL server 2008

I have query that that references a dynamic calander table, it updates aged periods based on the current date. I am using this table in another query, it sum's sales value based on aged QTR's. These values will update as time passes and I can assign colmn names such as 'AGED1Q' but I would like to find a way to use the actual period name from the calander table. Any sugestions will be greatly appreciated.
Current query is below, I have added a few comment lines where I would like to make the changes. Thanks again
SELECT dbo.CUSTOMER_ORDER.CUSTOMER_ID AS CUST_ID,
SUM(CASE WHEN dbo.UCC_CALENDAR_TODAY_BASED_AGING.QTRs_AGED = -4 THEN dbo.CUST_ORDER_LINE.TOTAL_AMT_ORDERED ELSE 0 END) AS '-4', --As MAX(dbo.UCC_CALENDAR_TODAY_BASED_AGING.QTR) WHERE dbo.UCC_CALENDAR_TODAY_BASED_AGING.QTRs_AGED = -4
SUM(CASE WHEN dbo.UCC_CALENDAR_TODAY_BASED_AGING.QTRs_AGED = -3 THEN dbo.CUST_ORDER_LINE.TOTAL_AMT_ORDERED ELSE 0 END) AS '-3', --As MAX(dbo.UCC_CALENDAR_TODAY_BASED_AGING.QTR) WHERE dbo.UCC_CALENDAR_TODAY_BASED_AGING.QTRs_AGED = -3
SUM(CASE WHEN dbo.UCC_CALENDAR_TODAY_BASED_AGING.QTRs_AGED = -2 THEN dbo.CUST_ORDER_LINE.TOTAL_AMT_ORDERED ELSE 0 END) AS '-2', --As MAX(dbo.UCC_CALENDAR_TODAY_BASED_AGING.QTR) WHERE dbo.UCC_CALENDAR_TODAY_BASED_AGING.QTRs_AGED = -2
SUM(CASE WHEN dbo.UCC_CALENDAR_TODAY_BASED_AGING.QTRs_AGED = -1 THEN dbo.CUST_ORDER_LINE.TOTAL_AMT_ORDERED ELSE 0 END) AS '-1', --As MAX(dbo.UCC_CALENDAR_TODAY_BASED_AGING.QTR) WHERE dbo.UCC_CALENDAR_TODAY_BASED_AGING.QTRs_AGED = -1
SUM(CASE WHEN dbo.UCC_CALENDAR_TODAY_BASED_AGING.QTRs_AGED IN(-4, -3, -2, -1) THEN dbo.CUST_ORDER_LINE.TOTAL_AMT_ORDERED ELSE 0 END) AS 'TOTAL'
FROM dbo.CUSTOMER_ORDER LEFT OUTER JOIN
dbo.UFC_Calander LEFT OUTER JOIN
dbo.UCC_CALENDAR_TODAY_BASED_AGING ON dbo.UFC_Calander.DAY = dbo.UCC_CALENDAR_TODAY_BASED_AGING.DATES ON
dbo.CUSTOMER_ORDER.ORDER_DATE = dbo.UFC_Calander.DAY LEFT OUTER JOIN
dbo.CUSTOMER ON dbo.CUSTOMER_ORDER.CUSTOMER_ID = dbo.CUSTOMER.ID RIGHT OUTER JOIN
dbo.PART RIGHT OUTER JOIN
dbo.CUST_ORDER_LINE ON dbo.PART.ID = dbo.CUST_ORDER_LINE.PART_ID ON dbo.CUSTOMER_ORDER.ID = dbo.CUST_ORDER_LINE.CUST_ORDER_ID
WHERE (dbo.CUSTOMER_ORDER.STATUS <> 'x') AND dbo.UCC_CALENDAR_TODAY_BASED_AGING.QTRs_AGED IN(-4, -3, -2, -1)
GROUP BY dbo.CUSTOMER_ORDER.CUSTOMER_ID
HAVING (dbo.CUSTOMER_ORDER.CUSTOMER_ID <> 'UNFOCO') AND (dbo.CUSTOMER_ORDER.CUSTOMER_ID <> 'QUOTE')
ORDER BY TOTAL DESC

SQL group by count across two tables

I have got two tables called baseline and revisits
baseline
formid-------NoOfIssues
1--------------3
2--------------4
3--------------5
revisits
id------formid-------NoOfIssues-----------date--------------fid
1---------2--------------4-------------5/06/2016------------1
2---------3--------------3-------------15/06/2016-----------1
3---------1--------------4-------------20/07/2016-----------1
4---------1--------------3-------------25/07/2016-----------1
5---------2--------------5-------------28/07/2016-----------1
6---------1--------------5-------------01/06/2016-----------1
7---------3--------------8-------------21/02/2016-----------1
8---------3--------------2-------------21/02/2016-----------2
These tables are joined by 'formid'.
I need to compare number of issues in baseline vs revisits(only first) and get a count as reduced, increased or equal
Based upon the above table i am expecting the following, for example across all three baseline entries no equals were found comparing NoOfissues in first revisit against same formid, but 1 equal and 2 increased were found
Addition: if same date and same formid is found than take the lower fid, so in the last two rows of revisits table both formid and date are equal but need to consider the lower formid which is 1
status----------Count
reduced----------0
equal------------1
increased--------2
I'm not familiar with intersystems-cache, but you can see if the following is valid SQL with that DB:
SELECT
CASE
WHEN BL.NoOfIssues = FR.NoOfIssues THEN 'equal'
WHEN BL.NoOfIssues > FR.NoOfIssues THEN 'reduced'
WHEN BL.NoOfIssues < FR.NoOfIssues THEN 'increased'
END AS status,
COUNT(*) AS Count
FROM
Baseline BL
INNER JOIN Revisits FR ON FR.formid = BL.formid
LEFT OUTER JOIN Revisits R ON
R.formid = BL.formid AND
(
R.date < FR.date OR
(R.date = FR.date AND R.fid > FR.fid)
)
WHERE
R.formid IS NULL
GROUP BY
CASE
WHEN BL.NoOfIssues = FR.NoOfIssues THEN 'equal'
WHEN BL.NoOfIssues > FR.NoOfIssues THEN 'reduced'
WHEN BL.NoOfIssues < FR.NoOfIssues THEN 'increased'
END
Some quick notes on your database though - You should probably decide on a standard of plural or singular table names and stick with it. Also, try to avoid common reserved words for object names, like date. Finally, if a revisit is basically the same as a visit, just on a later date then you should consider keeping them all in the same table.
I would do this in one row rather than three:
select sum(case when numissues = rcnt then 1 else 0 end) as equal,
sum(case when numissues > rcnt then 1 else 0 end) as reduced,
sum(case when numissues < rcnt then 1 else 0 end) as incrased
from (select b.form_id, b.numissues, count(r.form_id) as rcnt
from baseline b left join
revisits r
on b.form_id = r.form_id
group by b.form_id, b.numissues
) br;
I would use a window function to get the number of ussues at the minimum date, then compare that to the baseline number of issues
select
case when baseline.NoOfIssues = rev.NoOfIssues then 'equal'
when baseline.NoOfIssues > rev.NoOfIssues then 'reduced'
when baseline.NoOfIssues < rev.NoOfIssues then 'increased'
end as status,
count(*) as count
from baseline
inner join(
select
formid,
case when date = min(date) over(partition by formid) then NoOfIssues else null end as first_rev_issues
from revisits
) rev
on baseline.formid = rev.formid
group by
case when baseline.NoOfIssues = rev.NoOfIssues then 'equal'
when baseline.NoOfIssues > rev.NoOfIssues then 'reduced'
when baseline.NoOfIssues < rev.NoOfIssues then 'increased'
end
Or like this:
WITH CTE AS
(
SELECT
BL.FORMID,
BL.NOOFISSUES AS BLI,
RV.NOOFISSUES AS RVI,
RV.DATE,
ROW_NUMBER() OVER(PARTITION BY BL.FORMID ORDER BY RV.DATE) AS RN
FROM Baseline BL
INNER JOIN Revisits RV ON RV.FORMID = BL.FORMID
)
SELECT COALESCE(SUM(CASE WHEN RVI > BLI THEN 1 END), 0) AS INCREASED,
COALESCE(SUM(CASE WHEN RVI < BLI THEN 1 END), 0) AS DECREASED,
COALESCE(SUM(CASE WHEN RVI = BLI THEN 1 END), 0) AS EQUAL
FROM CTE
WHERE RN=1;

SQL Query: Cannot perform aggregate functions on sub queries

I have the following SQL query
SELECT
[Date],
DATENAME(dw,[Date]) AS Day,
SUM(CASE WHEN ChargeCode IN (SELECT ChargeCode FROM tblChargeCodes WHERE Chargeable = 1) THEN Units ELSE 0 END) ChargeableTotal,
SUM(CASE WHEN ChargeCode IN (SELECT ChargeCode FROM tblChargeCodes WHERE Chargeable = 0) THEN Units ELSE 0 END) NotChargeableTotal,
SUM(Units) AS TotalUnits
FROM
tblTimesheetEntries
WHERE
UserID = 'PJW'
AND Date >= '2013-01-01'
GROUP BY
[Date]
ORDER BY
[Date] DESC;
But I get the error message:
Cannot perform an aggregate function on an expression containing an aggregate or a subquery.
Because I am using sub queries in the Case Else Summation.
How can I revise my query to get 2 x Sums of [Units] one for Chargeable = true, and one for Chargeable = false, even though the Chargeable field is in a different table to all the other information. The two tables are linked by ChargeCode which appears in both tblTimesheetEntries and tblChargeCodes.
Have you tried joining the tables on the chargeCode:
SELECT e.[Date],
DATENAME(dw,e.[Date]) AS Day,
SUM(CASE WHEN c.Chargeable = 1 THEN e.Units ELSE 0 END) ChargeableTotal,
SUM(CASE WHEN c.Chargeable = 0 THEN e.Units ELSE 0 END) NotChargeableTotal,
SUM(e.Units) AS TotalUnits
FROM tblTimesheetEntries e
LEFT JOIN tblChargeCodes c
on e.ChargeCode = c.ChargeCode
WHERE e.UserID = 'PJW'
AND e.Date >= '2013-01-01'
GROUP BY e.[Date]
ORDER BY e.[Date] DESC;

SQL Select Sum Query

I need to complete the following but need to SUM on EntryValue where EntryType= 'C' - EntryType = 'D' but not sure where to include it.
SELECT SUM(EntryValue) AS EntryValue, Left(Warehouse,1) AS Branch
FROM GenJournalDetail INNER JOIN InvJournalCtl ON (GenJournalDetail.GlPeriod = InvJournalCtl.LedgPeriod) AND (GenJournalDetail.GlYear = InvJournalCtl.YearPostLedg) AND (GenJournalDetail.Journal = InvJournalCtl.GlJournal)
WHERE GenJournalDetail.GlCode = 'Z5207550' AND GlYear = '2011' AND GlPeriod = '10'
GROUP BY Left(InvJournalCtl.Warehouse,1)
Select it then group it;
SELECT SUM(EntryValue) AS EntryValueTotal, EntryType, Left(Warehouse,1) AS Branch
...
GROUP BY Left(InvJournalCtl.Warehouse,1), EntryType
Edit I didn't realise you wanted to deduct;
SELECT
SUM(CASE EntryType
WHEN 'C' THEN EntryValue
ELSE 0 END
)
-
SUM(CASE EntryType
WHEN 'D' THEN EntryValue
ELSE 0 END
) AS EntryValueTotal,
Left(Warehouse,1) AS Branch
FROM GenJournalDetail
INNER JOIN InvJournalCtl ON (GenJournalDetail.GlPeriod = InvJournalCtl.LedgPeriod) AND (GenJournalDetail.GlYear = InvJournalCtl.YearPostLedg) AND (GenJournalDetail.Journal = InvJournalCtl.GlJournal)
WHERE
GenJournalDetail.GlCode = 'Z5207550' AND GlYear = '2011' AND GlPeriod = '10'
GROUP BY Left(InvJournalCtl.Warehouse,1)
If you need to sum the column add it in the select and group:
SELECT SUM(EntryValue) AS TotEntry, ...
...
GROUP BY ...
you don't need to group an aggregated column; you need to filter query too?