Transact-SQL Sub Query - sql

I'm struggling to find the logic of how to accomplish a sub query, or at least that's what I think is required! I'll show what I have:
SELECT CH.SFA,
convert(datetime, RE.START_DATE, 103) AS 'START DATE',
Count(Distinct CH.CHNO) AS 'TOTAL CH',
Count(CH.STATUS) AS 'COMPLETED CH',
count(distinct CH.CHNO + CH.STATUS) As 'COMPLETED CH2'
FROM CUSTOMER.dbo. CH CH, CUSTOMER.dbo.RE RE
WHERE
RE.SFA = CH.SFA
GROUP BY
CH.SFA, RE.START_DATE
What I am trying to do is where I have COMPLETED CH2 I need to specify that it ends with a C, the Status Column is either blanks or C's and by doing a distinct count of CHNO and C together give me the result I need but I cannot for the life of me find out how to write it!
I am using Microsoft Query to take the data from its source straight into the Excel spreadsheet.
Many thanks for taking a look.

Been ages since I've used MS Query so I'm fuzzy on syntax, but this is the general idea of how to write a subquery containing a WHERE clause and an aggregation to get you started:
SELECT
CH.SFA,
convert(datetime, RE.START_DATE, 103) AS 'START DATE',
Count(DISTINCT CH.CHNO) AS 'TOTAL CH',
Count(CH.STATUS) AS 'COMPLETED CH',
CCH.COMPLETED_CH2 AS 'COMPLETED CH2'
FROM CUSTOMER.dbo.CH CH
INNER JOIN CUSTOMER.dbo.RE RE
ON RE.SFA = CH.SFA
LEFT JOIN (
SELECT SFA, COUNT(DISTINCT CH.CHNO) AS COMPLETED_CH2
FROM CUSTOMER.dbo.CH
WHERE STATUS = 'C'
GROUP BY SFA
) AS CCH
ON RE.SFA = CCH.SFA
GROUP BY CH.SFA, RE.START_DATE

if you just want to know count of records where CH.STATUS = 'C' than add another COUNT statement with CASE logic.
COUNT(CASE WHEN CH.STATUS = 'C' then 1 else null end) as 'COMPLETED CH2'
when combining COUNT and CASE statement remember to have NULL for ELSE statement, otherwise all rows will be counted.
as an alternative you can do it with a SUM
SUM(CASE WHEN CH.STATUS = 'C' then 1 else 0 end) as 'COMPLETED CH2'

Related

aggregate function error in case expression

I have this query
SELECT mylearning.Employee_Id,
case
when max(case when not mylearning.CourseStatusTXT = 'Completed' then 1 else 0 end) = 0 then '2018 Complete'
when max(case when mylearning.CourseStatusTXT in ('Started', 'Not Started') then 1 else 0 end) = 1 then '2018 Not Complete'
end as Completion_Status
FROM Analytics.myLearning_Completions as mylearning inner join Analytics.Workday WD on mylearning.Employee_ID = WD.Employee_ID
And I want to add a condition to the first when statement to make it like this
when max(case when not mylearning.CourseStatusTXT = 'Completed' then 1 else 0 end) = 0
and WD.Adjusted_Hire_Date like '2019% '
and mylearning.CourseTimeCompletedH < cast (WD.Adjusted_Hire_Date as date format 'YYYY/MM/DD') +7
then '2018 Complete'
but I keep getting this error
Executed as Single statement. Failed [3504 : HY000] Selected non-aggregate values must be part of the associated group.
Elapsed time = 00:00:00.069
How can I fix it?
Like a couple others mentioned, you are trying to mix grouped data with non-aggregated data in your calculation, which is why you're getting the 3504 error. You need to either include the referenced columns in your GROUP BY or include them inside an aggregate function (i.e. MAX).
I'm not 100% sure if this is what you're after, but hopefully it can help you along.
SELECT
mylearning.Employee_Id,
CASE
WHEN
MAX(CASE WHEN NOT mylearning.CourseStatusTXT = 'Completed' THEN 1 ELSE 0 END) = 0 AND
WD.Adjusted_Hire_Date like '2019% ' AND
-- Check if most recently completed course is before Hire (Date + 1 week)
MAX(mylearning.CourseTimeCompletedH) <
CAST(WD.Adjusted_Hire_Date AS DATE FORMAT 'YYYY/MM/DD') + 7
THEN '2018 Complete' -- No incomplete learnings
WHEN MAX(
CASE WHEN mylearning.CourseStatusTXT IN ('Started', 'Not Started') THEN 1 ELSE 0 END
) = 1 THEN '2018 Not Complete' -- Started / Not Started learnings exist
END AS Completion_Status
FROM Analytics.myLearning_Completions as mylearning -- Get learning info
INNER JOIN Analytics.Workday WD on mylearning.Employee_ID = WD.Employee_ID -- Employee info
GROUP BY mylearning.Employee_Id, WD.Adjusted_Hire_Date
This will give you a summary per employee, with a couple assumptions:
Assuming employee_ID value in Analytics.Workday is a unique value (one-to-one join), to use WD.Adjusted_Hire_Date in your comparisons, you just need to include it in the GROUP BY.
Assuming you have multiple courses per employee_Id, in order to use mylearning.CourseTimeCompletedH in your comparisons, you'd need to wrap that in an aggregate like MAX.
The caveat here is that the query will check if the most recently completed course per employee is before the "hire_date" expression, so I'm not sure if that's what you're after.
Give it a try and let me know.
The issue here is that you are mixing detail row by row information in the same query as group or aggregated data. Aggregated data will output a single value for all the rows unless you have a group by clause. If you have a group by clause then it will output a single value for each group. When you are grouping you can also include any values that are in the group by clause since they will be unique for the group.
if you want this data for each employee, then you could group by employee_id. Any other data would need to also be an aggregate like Max(Adjusted_Hire_Date)
Maybe this is what you want?
SELECT
mylearning.employee_id
, case
when CourseStatusTXT = 'Completed' and WD.Adjusted_Hire_Date like '2019%'
and mylearning.CourseTimeCompletedH < cast (WD.Adjusted_Hire_Date as date format 'YYYY/MM/DD') +7
then '2018 Complete'
else '2018 Not Complete'
end CompletionStatus
FROM myLearning_Completions mylearning, Workday WD
WHERE mylearning.employee_id = WD.employee_id

compare two different date ranges sales in two columns

I want to compare two different date ranges sales in two columns.. I am using query below but its giving wrong sales.. please correct my query
select s1.Itm_cd,s1.Itm_Name,Sum(S1.amount),Sum(s2.amount)
from salestrans s1,salestrans s2
where s1.Itm_cd = S2.Itm_cd
and S1.Tran_dt between '20181101' and'20181130'
and S2.Tran_dt between '20171101' and '20171130'
group by s1.Itm_cd,s1.Itm_Name
Order by s1.Itm_cd
I suspect that you want conditional aggregation here:
WITH cte AS (
SELECT
s1.Itm_cd,
s1.Itm_Name,
SUM(CASE WHEN s1.Tran_dt BETWEEN '20181101' AND '20181130'
THEN s1.amount ELSE 0 END) AS sum_2018,
SUM(CASE WHEN s1.Tran_dt BETWEEN '20171101' AND '20171130'
THEN s1.amount ELSE 0 END) AS sum_2017
FROM salestrans s1
GROUP BY
s1.Itm_cd,
s1.Itm_Name
)
SELECT
Itm_cd,
Itm_Name,
sum_2018,
sum_2017,
CASE WHEN COALESCE(sum_2017, 0) <> 0
THEN FORMAT(100.0 * (sum_2018 - sum_2017) / sum_2017, 'N', 'en-us')
ELSE 'NA' END AS growth_pct
FROM cte
ORDER BY
Itm_cd;
Please try the following
select s1.Itm_cd,s1.Itm_Name,Sum(S1.amount),Sum(s2.amount)
from salestrans s1,salestrans s2
where s1.Itm_cd = S2.Itm_cd
and Convert(Varchar(10),S1.Tran_dt,112) between '20181101' and'20181130'
and Convert(Varchar(10),S2.Tran_dt,112) between '20171101' and '20171130'
group by s1.Itm_cd,s1.Itm_Name
Order by s1.Itm_cd
Here the logic is that in right side while comparision you are providing only date and not any separator and time. The same way should be applied to the column in left side for comparision.
if(Convert(Varchar(10), getdate(),112) = '20181224')
print 'Matched'
else
print 'Not Matched'
if(getdate() = '20181224')
print 'Matched'
else
print 'Not Matched'
Here the output is Matched for first and Not Matched because in first case both side same format has been taken for comparison.

Complex Query SQL

I am currently working on an sql query where i am receiving data in three different rows, i really need the data in one single row
SELECT
substring(D.F1056, patindex('%[^0]%',D.F1056), 10) as Shop_Number,
REPLACE(CONVERT(VARCHAR(10), D.F254, 103), '/', '') AS Todays_Date,
SDP_TAB.F03 as Rayon,
[SDP_TAB].F04 as Famille,
CASE
WHEN D.F1034=3 THEN SUM(D.F64)
ELSE 0
END as Qty_Sold ,
CASE
WHEN D.F1034=3 THEN convert(DOUBLE PRECISION, SUM(D.F65)*100) * 10
ELSE 0
END as chiffre_daffaire_Caisse,
0 as montant_remisse_caisse,
CASE
WHEN D.F1034=3011 THEN SUM(D.F64)
WHEN D.F1034=3012 THEN SUM(D.F64)
ELSE 0
END as Qty_retour,
CASE
WHEN D.F1034=3011 THEN SUM(D.F65)
WHEN D.F1034=3012 THEN SUM(D.F65)
ELSE 0
END as Montant_Retour,
0 as Quantity,
CASE
WHEN D.F1034=3102 THEN SUM(D.F64)
ELSE 0
END as ClientCount,
F1034
FROM
[dbo].[RPT_ITM_D] D
LEFT OUTER JOIN [dbo].[POS_TAB] ON (D.F01=POS_TAB.F01)
LEFT OUTER JOIN [dbo].SDP_TAB ON (POS_TAB.F04=SDP_TAB.F04)
where
D.F1034 IN (3,3012,3011,3102)
AND
D.F254 = convert(varchar, getdate(), 101)
group by D.F1056,D.F254,SDP_TAB.F03,SDP_TAB.F04,D.F1034
My data is being populated as below
I am having three rows since i am having several condition for field F1034
Is there a way to have the expected result as below
Try this. (Using max with calculated columns)
With resultData as (
—Put your original query here
)
Select Shop_Number, Todays_Date,rayon,famille
,max(Qty_Sold) Qty_Sold, max(chiffre_daffaire_Caisse) chiffre_daffaire_Caisse
,max(montant_remisse_caisse) montant_remisse_caisse,max(Qty_retour) Qty_retour,max(Montant_Retour) Montant_Retour
,max(Quantity)Quantity,max(ClientCount) ClientCount,max(F1034) F1034
From resultData
group by Shop_Number, Todays_Date,rayon,famille

SQL Return Value of Zero so filter remains

The query below works great except for one thing.
I am using the column as "WorkStream" as a filter in my end user report. Since it is a filter I need it to be there regardless of whether or not the query returns anything.
Of course I have to use the where clause as a filter within the query so that only certain tasks are returned.
The trouble is that when I have a workstream without any task_Significance items the workstream is removed and the report filter (Workstream) does not work.
What I need is a table that looks like this but if I have a missing workstream then it would put "0's" all the way across. But only if the workstream is missing would it return ONLY the filter name (so the report would have the filter and just show and empty table below the filter in the final report.
This is my code:
SELECT
p.[Workstream]
,T.[Task_Significance]
,COUNT(1) AS Total
,SUM(case when T.[TaskPercentCompleted] >= 100 then 1 else 0 end) AS Actual
,SUM(case when T.[TaskFinishDate] <= DATEADD(DAY, 8-DATEPART(DW, GETDATE()), Convert(date,getdate())) then 1 else 0 end) AS Planned
FROM [psmado].[dbo].[MSP_EpmProject_UserView] as P
join [PSMADO].[dbo].[MSP_EpmTask_UserView] as T
On T.[projectUID] = P.[projectUID]
WHERE
[Task_Significance] IN('Application', 'Data', 'Interface', 'End User Compute', 'Network', 'Compute Package', 'Data Center', 'CREWS Sites', 'App Design Review', 'Infra Design Review')
GROUP BY p.[EnterpriseProjectTypeUID], p.[Workstream],T.[Task_Significance]
Is there anyway to do this?
Use a left join and move your where clause to the on clause. This will return all p.[Workstream] and those t.Task_Significance that are in your in() list.
You can use coalesce() or isnull() to substitute a value for null when there are no matching t.Task_Significance.
select
p.[Workstream]
, coalesce(T.[Task_Significance], 'none') as Task_Significance
, count(1) as Total
, sum(case when T.[TaskPercentCompleted] >= 100 then 1 else 0 end) as Actual
, sum(case when T.[TaskFinishDate]
<= dateadd(day, 8 - datepart(dw, getdate()), Convert(date, getdate()))
then 1 else 0 end) as Planned
from [psmado].[dbo].[msp_EpmProject_UserView] as P
left join [psmado].[dbo].[msp_EpmTask_UserView] as T
on T.[projectuid] = P.[projectuid]
and [Task_Significance] in ('Application', 'Data', 'Interface'
, 'End User Compute', 'Network', 'Compute Package', 'Data Center'
, 'crews Sites', 'App Design Review', 'Infra Design Review')
group by
p.[EnterpriseProjectTypeuid]
, p.[Workstream]
, T.[Task_Significance]

Using CASE in SQL Server - getting "because it is not contained in either an aggregate function or the GROUP BY clause."

I want to show the days since the last customer order from a certain storenumber, I have been told to use CASE.
I don't want to use MAX or MIN because it may ignore other records for said customer.
SELECT ms.CustomerID AS email,
AS last_txn_days_online,
CASE
WHEN ST2.StoreNumber != '100799' THEN CEILING(Round(DateDiff(DAY, Min(st2.PurchaseDate), Max(st2.PurchaseDate)) / NULLIF(Count(st2.CustomerID) - 1, 0),0))
ELSE NULL
END AS last_txn_days_instore
FROM [Not MS] ms
LEFT JOIN [ORDER_HEADER] st2 ON ms.CustomerID = st2.CustomerID
GROUP BY MS.CustomerID
I think you want conditional aggregation. The query would look something like this:
SELECT ms.CustomerID AS email,
CEILING(Round(DateDiff(DAY,
Min(CASE WHEN ST2.StoreNumber <> '100799' THEN st2.PurchaseDate END),
M MAX(CASE WHEN ST2.StoreNumber <> '100799' THEN st2.PurchaseDate END)
) / NULLIF(Count(CASE WHEN ST2.StoreNumber <> '100799' THEN st2.CustomerID END) - 1, 0), 0
)
)
END AS last_txn_days_instore
FROM [Not MS] ms LEFT JOIN
[ORDER_HEADER] st2
ON ms.CustomerID = st2.CustomerID
GROUP BY MS.CustomerID