Sub select SQL query using where and groupby - sql

When I try below code in SQL Server,
SELECT
dbo.Category.CatNo as Category,
dbo.Category.Categaory as Name,
(select SUM(dbo.SALES.SELLINC) where (dbo.SALES.DATE BETWEEN '2016-07-17' AND '2016-07-23')) AS ActualSales,
(select SUM(dbo.SALES.SELLINC) where (dbo.SALES.DATE BETWEEN '2015-07-19' AND '2015-07-25')) AS LastYrVariance,
(select SUM(dbo.SALES.SELLINC) where (dbo.SALES.DATE BETWEEN '2016-01-01' AND '2016-09-05')) AS YrToDateActual,
(select SUM(dbo.SALES.SELLINC) where (dbo.SALES.DATE BETWEEN '2015-01-01' AND '2015-09-05')) AS LastYrToDateActual
FROM dbo.Category INNER JOIN
dbo.Dissection ON dbo.Category.CatNo = dbo.Dissection.CatNo INNER JOIN
dbo.Division ON dbo.Dissection.DivNo = dbo.Division.ID INNER JOIN
dbo.Departments ON dbo.Dissection.DeptNo = dbo.Departments.DeptID INNER JOIN
dbo.SALES ON dbo.Dissection.DissNo = dbo.SALES.CODE
WHERE (dbo.SALES.BRN = 1)
GROUP BY dbo.Category.CatNo, dbo.Category.Categaory
ORDER BY dbo.Category.CatNo
I get below error message,
Msg 8120, Level 16, State 1, Line 2
Column 'dbo.Category.CatNo' is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY
clause.
Any help please?

The subqueries have a select clause, with a Sum() function which attempts to Sum the values from a column of a table that is not mentioned in the Subquery. (The subqueries have no FROM clause). These subqueries should not be subqueries, they just need to be Sum() expressions.
And you should check into using aliases to simplify your SQL scripts.
SELECT c.CatNo Category, c.Categaory Name,
sum(case when s.Date between '2016-07-17' and '2016-07-23'
then s.SELLINC else 0 end) ActualSales,
sum(case when s.Date between '2015-07-25' and '2015-07-19'
then s.SELLINC else 0 end) LastYrVariance,
sum(case when s.Date between '2016-01-01' and '2016-09-05'
then s.SELLINC else 0 end) YrToDateActual,
sum(case when s.Date between '2015-01-01' AND '2015-09-05'
then s.SELLINC else 0 end) LastYrToDateActual
FROM dbo.Category c
join dbo.Dissection d on d.CatNo = c.CatNo
join dbo.Division v on v.Id = d.DivNo
join dbo.Departments p on p.DeptID = d.DeptNo
join dbo.SALES s on s.code = d.DissNo
WHERE s.BRN = 1
GROUP BY c.CatNo, c.Categaory
ORDER BY c.CatNo
to generate date range dynamically, as requested in comment, use datediff and dateAdd functions: I'm not sure what you meant by previous week (31 week 2016) or Last year same week (31 week 2015), so if this is not right, you can modify to correct.
SELECT c.CatNo Category, c.Categaory Name,
sum(case when s.Date between
dateAdd(week, datediff(week, 0, getdate())-52, 0)
and dateAdd(week, datediff(week, 0, getdate()), 0)
then s.SELLINC else 0 end) ActualSales,
sum(case when s.Date between '2015-07-25' and '2015-07-19'
then s.SELLINC else 0 end) LastYrVariance,
sum(case when s.Date between '2016-01-01' and '2016-09-05'
then s.SELLINC else 0 end) YrToDateActual,
sum(case when s.Date between '2015-01-01' AND '2015-09-05'
then s.SELLINC else 0 end) LastYrToDateActual
FROM dbo.Category c
join dbo.Dissection d on d.CatNo = c.CatNo
join dbo.Division v on v.Id = d.DivNo
join dbo.Departments p on p.DeptID = d.DeptNo
join dbo.SALES s on s.code = d.DissNo
WHERE s.BRN = 1
GROUP BY c.CatNo, c.Categaory
ORDER BY c.CatNo

I cant find a reason to generate that error in your query. Between you can simply those sub-queries by using CASE statements
SELECT dbo.category.catno AS Category,
dbo.category.categaory AS NAME,
Sum(CASE WHEN dbo.sales.date BETWEEN '2016-07-17' AND '2016-07-23' THEN dbo.sales.sellinc END) AS ActualSales,
Sum(CASE WHEN dbo.sales.date BETWEEN '2015-07-19' AND '2015-07-25' THEN dbo.sales.sellinc END) AS LastYrVariance,
Sum(CASE WHEN dbo.sales.date BETWEEN '2016-01-01' AND '2016-09-05' THEN dbo.sales.sellinc END) AS YrToDateActual,
Sum(CASE WHEN dbo.sales.date BETWEEN '2015-01-01' AND '2015-09-05' THEN dbo.sales.sellinc END) AS LastYrToDateActual
FROM dbo.category
INNER JOIN dbo.dissection
ON dbo.category.catno = dbo.dissection.catno
INNER JOIN dbo.division
ON dbo.dissection.divno = dbo.division.id
INNER JOIN dbo.departments
ON dbo.dissection.deptno = dbo.departments.deptid
INNER JOIN dbo.sales
ON dbo.dissection.dissno = dbo.sales.code
WHERE ( dbo.sales.brn = 1 )
GROUP BY dbo.category.catno,
dbo.category.categaory
ORDER BY dbo.category.catno

can you try with the below query.
SELECT Category.CatNo as Category, Category.Categaory as Name,
(select SUM(SALES.SELLINC) where (SALES.DATE BETWEEN '2016-07-17' AND '2016-07-23')) AS ActualSales,
(select SUM(SALES.SELLINC) where (SALES.DATE BETWEEN '2015-07-19' AND '2015-07-25')) AS LastYrVariance,
(select SUM(SALES.SELLINC) where (SALES.DATE BETWEEN '2016-01-01' AND '2016-09-05')) AS YrToDateActual,
(select SUM(SALES.SELLINC) where (SALES.DATE BETWEEN '2015-01-01' AND '2015-09-05')) AS LastYrToDateActual
FROM dbo.Category INNER JOIN
dbo.Dissection ON Category.CatNo = Dissection.CatNo INNER JOIN
dbo.Division ON Dissection.DivNo = Division.ID INNER JOIN
dbo.Departments ON Dissection.DeptNo = Departments.DeptID INNER JOIN
dbo.SALES ON Dissection.DissNo = SALES.CODE
WHERE (SALES.BRN = 1)
GROUP BY Category.CatNo, Category.Categaory
ORDER BY Category.CatNo

Related

Get first and last Order and the highest value Item in each order for each Customer, all of which are separate tables

I need to find the first and last Order for each Customer by OrderDate, and the name and SKU of the item with the highest business volume in each of those orders. For reference, the Customer table has >150k records, and Orders and OrderDetails (these are the Items) a lot more.
Note: Both Orders and their respective items should be in the same row as the Customer
Orders
OrderID OrderDate CustomerID BusinessVolumeTotal Subtotal
13212 '2021-09-06' 512312 500.00 25.60
OrderDetails
OrderID ItemCode ItemDescription BusinessVolume
13212 'SKW-BS' 'Some item' 450.00
In my first query I attempted to stick to joining instead of subqueries, which resulted in this
select distinct(c.CustomerID), c.FirstName + ' ' + c.LastName as Name,
cs.CustomerStatusDescription as Status,
ct.CustomerTypeDescription as Type, pv.Volume80 as G3,
fo.OrderID,fo.OrderDate,fo.SubTotal,fo.Country, fod.ItemCode, fod.ItemDescription, fopt.PriceTypeID,
lo.OrderID,lo.OrderDate,lo.SubTotal,lo.Country, lod.ItemCode, lod.ItemDescription, lopt.PriceTypeID
from Customers c
left join CustomerTypes ct on ct.CustomerTypeID = c.CustomerTypeID
left join CustomerStatuses cs on cs.CustomerStatusID = c.CustomerStatusID
left join PeriodVolumes pv on pv.CustomerID = c.CustomerID
left join Orders fo on fo.CustomerID = c.CustomerID -- First Order
left join Orders lo on lo.CustomerID = c.CustomerID -- Last Order
left join OrderDetails fod on fod.OrderID = fo.OrderID
left join OrderDetails lod on lod.OrderID = lo.OrderID
left join PriceTypes fopt on fo.PriceTypeID = fopt.PriceTypeID
left join PriceTypes lopt on lo.PriceTypeID = lopt.PriceTypeID
where c.CustomerStatusID in (1,2)
and c.CustomerTypeID in (2,3)
and pv.PeriodTypeID = 2
/* First Order */
and fo.OrderID = (select top 1(OrderID) from Orders where CustomerID = c.CustomerID and OrderStatusID>=7 order by OrderDate )
and fod.ItemID = (select top 1(ItemID) from OrderDetails where OrderID = fo.OrderID order by BusinessVolume)
/* Last Order */
and lo.OrderID = (select top 1(OrderID) from Orders where CustomerID = c.CustomerID and OrderStatusID>=7 order by OrderDate desc)
and lod.ItemID = (select top 1(ItemID) from OrderDetails where OrderID = lo.OrderID order by BusinessVolume desc)
and pv.PeriodID = (select PeriodID from Periods where PeriodTypeID=2 and StartDate <= #now and EndDate >= #now)
But this ends up executing for about 6-7 minutes. From the explain plan, it looks like most of it is taken up by a Key Lookup for Orders based on OrderStatusID >= 7.
So I tried to use window functions to achieve the same:
select distinct(c.CustomerID), c.FirstName + ' ' + c.LastName as Name, cs.CustomerStatusDescription as Status,
ct.CustomerTypeDescription as Type, pv.Volume80 as G3,
fal.*
from Customers c
left join CustomerTypes ct on ct.CustomerTypeID = c.CustomerTypeID
left join CustomerStatuses cs on cs.CustomerStatusID = c.CustomerStatusID
left join PeriodVolumes pv on pv.CustomerID = c.CustomerID
left join(
select
CustomerID,
max(case when MinDate = 1 then OrderID end) FirstOrderID,
max(case when MinDate = 1 then OrderDate end) FirstOrderDate,
max(case when MinDate = 1 then BusinessVolumeTotal end) FirstBVTotal,
max(case when MinDate = 1 then PriceTypeDescription end) FirstPriceType,
max(case when MinDate = 1 then ItemCode end) FirstItemCode,
max(case when MinDate = 1 then ItemDescription end) FirstItemDescription,
max(case when MaxDate = 1 then OrderID end) LastOrderID,
max(case when MaxDate = 1 then OrderDate end) LastOrderDate,
max(case when MaxDate = 1 then BusinessVolumeTotal end) LastBVTotal,
max(case when MaxDate = 1 then PriceTypeDescription end) LastPriceType,
max(case when MaxDate = 1 then ItemCode end) LastItemCode,
max(case when MaxDate = 1 then ItemDescription end) LastItemDescription
from
(
select distinct o.CustomerID,
o.OrderID,
o.OrderDate,
o.BusinessVolumeTotal,
PT.PriceTypeDescription,
RANK() over (partition by o.CustomerID order by OrderDate) as MinDate,
RANK() over (partition by o.CustomerID order by OrderDate desc) as MaxDate,
FIRST_VALUE(ItemCode) over (partition by od.OrderID order by BusinessVolume desc) as ItemCode,
FIRST_VALUE(ItemDescription) over (partition by od.OrderID order by BusinessVolume desc) as ItemDescription
from Orders o
left join OrderDetails od on od.OrderID = o.OrderID
left join PriceTypes PT on o.PriceTypeID = PT.PriceTypeID
where o.OrderStatusID >= 7
) fal
group by CustomerID
) fal on c.CustomerId = fal.CustomerID
where c.CustomerStatusID in (1,2)
and c.CustomerTypeID in (2,3)
and pv.PeriodTypeID = 2
/* CurrentG3 */
and pv.PeriodID = (select PeriodID from Periods where PeriodTypeID=2 and StartDate <= #now and EndDate >= #now)
Alas, this ended up executing even longer. I need a way to optimize this if possible.
Secondary query
I do also need a count and sum of volume per Order in the last 3, 6 and 12 months. I currently do this programatically as secondary queries after the original returns a result, and I forward the CustomerIDs, like this:
select count(OrderID) as Cnt, sum(BusinessVolumeTotal) as Bv, CustomerID
from Orders where OrderStatusID > 6 and OrderTypeID in (1,4,8,11)
and OrderDate >= #timeAgo and CustomerID in #ids group by CustomerID
Times 3, because 3, 6 and 12 months. Ideally, I would also like to make this part of the original but I don't really have a good idea on how to do it, especially with how convoluted the joining is with the orders.
So ideally I'd end up with a result table like this
CustomerID Name CustomerStatus CustomerType FirstOrderID FirstOrderDate FirstBVTotal FirstItemCode FirstItemDesc FirstPriceType LastOrderID LastOrderDate LastBVTotal LastItemCode LastItemDesc LastPriceType ThreeMonthCount ThreeMonthTotal SixMonthCount SixMonthTotal TwelveMonthCount TwelveMonthTotal
512312 'Jane Doe' 'Active' 'Retail' 13212 '2020-06-06' 50.00 'Item1' 'Item 1 desc' 'Retail' 14321 '2021-09-01' 200.00 'Item2' 'Item 2 desc' 'Retail' 45 4305.00 76 8545.60 183 21542.95
Any help and advice on how to optimize or reduce the query, as well as anything you believe Im doing wrong would be GREATLY appreciated.
P.S. I don't know if the title is fitting and if I'd be able to change it later, it's been a while since I've used SO to ask a question.
UPDATE
Actual Execution Plan for Query 1:
https://www.brentozar.com/pastetheplan/?id=SJd56RSmK
Actual Execution Plan for Query 2:
https://www.brentozar.com/pastetheplan/?id=BJ7QHk87Y
I think you need to keep in mind two main points with this type of query:
The key to good performance with window functions is to not introduce an unnecessary sort. So while you can use ROW_NUMBER to get the first order in either direction, you should not use another opposing ROW_NUMBER to get the last. Rather use LEAD to check if the next row exists, thereby telling you if this is the last row. You can then use conditional aggregation.
There are generally two ways to calculate first/last: a row-numbering solution, as above, or an APPLY, which picks out the exact one you need.
I think that for the OrderDetails we should use an apply, because there are only two orders per customer that we need to find. This does need good indexing, so if OrderDetails is not well indexed, then you may want to switch to a row-numbering solution for this also.
select
c.CustomerID,
c.FirstName + ' ' + c.LastName as Name,
cs.CustomerStatusDescription as Status,
ct.CustomerTypeDescription as Type,
pv.Volume80 as G3,
o.FirstOrderID,
o.FirstOrderDate,
o.FirstSubTotal,
o.FirstCountry,
fod.ItemCode as FirstItemCode,
fod.ItemDescription as FirstItemDescription,
fopt.PriceTypeDescription as FirstPriceTypeDescription,
o.LastOrderID,
o.LastOrderDate,
o.LastSubTotal,
o.LastCountry,
lod.ItemCode as LastItemCode,
lod.ItemDescription as LastItemDescription,
lopt.PriceTypeDescription as LastPriceTypeDescription
from Customers c
left join CustomerTypes ct on ct.CustomerTypeID = c.CustomerTypeID
left join CustomerStatuses cs on cs.CustomerStatusID = c.CustomerStatusID
left join PeriodVolumes pv on pv.CustomerID = c.CustomerID
and pv.PeriodTypeID = 2
and pv.PeriodID = (
select top 1 PeriodID
from Periods p
where p.PeriodTypeID = 2
and p.StartDate <= #now
and p.EndDate >= #now
)
left join (
select
o.CustomerID,
min(case when rn = 1 then OrderID end) as FirstOrderId,
min(case when rn = 1 then OrderDate end) as FirstOrderDate,
min(case when rn = 1 then SubTotal end) as FirstSubTotal,
min(case when rn = 1 then Country end) as FirstCountry,
min(case when nx is null then OrderID end) as LastOrderId,
min(case when nx is null then OrderDate end) as LastOrderDate,
min(case when nx is null then SubTotal end) as LastSubTotal,
min(case when nx is null then Country end) as LastCountry,
count(case when o.OrderDate >= DATEADD(month, -3, GETDATE()) then 1 end) as ThreeMonthCount,
sum(case when o.OrderDate >= DATEADD(month, -3, GETDATE()) then BusinessVolumeTotal end) as ThreeMonthTotal,
count(case when o.OrderDate >= DATEADD(month, -6, GETDATE()) then 1 end) as SixMonthCount,
sum(case when o.OrderDate >= DATEADD(month, -6, GETDATE()) then BusinessVolumeTotal end) as SixMonthTotal,
count(case when o.OrderDate >= DATEADD(month, -12, GETDATE()) then 1 end) as TwelveMonthCount,
sum(case when o.OrderDate >= DATEADD(month, -12, GETDATE()) then BusinessVolumeTotal end) as TwelveMonthTotal
from (
select *,
ROW_NUMBER() over (partition by o.CustomerID order by OrderDate) as rn,
LEAD(OrderID) over (partition by o.CustomerID order by OrderDate) as nx
from Orders o
where o.OrderStatusID >= 7
and o.OrderTypeID in (1,4,8,11)
and o.OrderDate >= #timeAgo
) o
group by o.CustomerID
) o on o.CustomerID = c.CustomerID
outer apply (
select top 1
od.ItemCode,
od.ItemDescription
from OrderDetails od
order by od.BusinessVolume desc
where od.OrderID = o.FirstOrderId
) fod
outer apply (
select top 1
od.ItemCode,
od.ItemDescription
from OrderDetails od
order by od.BusinessVolume desc
where od.OrderID = o.LastOrderId
) lod
left join PriceTypes fopt on fopt.PriceTypeID = o.FirstPriceTypeID
left join PriceTypes lopt on lopt.PriceTypeID = o.LastPriceTypeID
where c.CustomerStatusID in (1,2)
and c.CustomerTypeID in (2,3);
I'm also going to give you a row-numbering version, as judging by your execution plan, it may actually be better. You need to try both
select
c.CustomerID,
c.FirstName + ' ' + c.LastName as Name,
cs.CustomerStatusDescription as Status,
ct.CustomerTypeDescription as Type,
pv.Volume80 as G3,
o.FirstOrderID,
o.FirstOrderDate,
o.FirstSubTotal,
o.FirstCountry,
o.FirstItemCode,
o.FirstItemDescription,
o.FirstPriceTypeDescription,
o.LastOrderID,
o.LastOrderDate,
o.LastSubTotal,
o.LastCountry,
o.LastItemCode,
o.LastItemDescription,
o.LastPriceTypeDescription
from Customers c
left join CustomerTypes ct on ct.CustomerTypeID = c.CustomerTypeID
left join CustomerStatuses cs on cs.CustomerStatusID = c.CustomerStatusID
left join PeriodVolumes pv on pv.CustomerID = c.CustomerID
and pv.PeriodTypeID = 2
and pv.PeriodID = (
select top 1 PeriodID
from Periods p
where p.PeriodTypeID = 2
and p.StartDate <= #now
and p.EndDate >= #now
)
left join (
select
o.CustomerID,
min(case when rn = 1 then o.OrderID end) as FirstOrderId,
min(case when rn = 1 then o.OrderDate end) as FirstOrderDate,
min(case when rn = 1 then o.SubTotal end) as FirstSubTotal,
min(case when rn = 1 then o.Country end) as FirstCountry,
min(case when rn = 1 then od.ItemCode end) as FirstItemCode,
min(case when rn = 1 then od.ItemDescription end) as FirstItemDescription,
min(case when rn = 1 then opt.PriceTypeDescription end) as FirstPriceTypeDescription,
min(case when nx is null then o.OrderID end) as LastOrderId,
min(case when nx is null then o.OrderDate end) as LastOrderDate,
min(case when nx is null then o.SubTotal end) as LastSubTotal,
min(case when nx is null then o.Country end) as LastCountry,
min(case when nx is null then od.ItemCode end) as LastItemCode,
min(case when nx is null then od.ItemDescription end) as LastItemDescription,
min(case when nx is null then opt.PriceTypeDescription end) as LastPriceTypeDescription,
count(case when o.OrderDate >= DATEADD(month, -3, GETDATE()) then 1 end) as ThreeMonthCount,
sum(case when o.OrderDate >= DATEADD(month, -3, GETDATE()) then BusinessVolumeTotal end) as ThreeMonthTotal,
count(case when o.OrderDate >= DATEADD(month, -6, GETDATE()) then 1 end) as SixMonthCount,
sum(case when o.OrderDate >= DATEADD(month, -6, GETDATE()) then BusinessVolumeTotal end) as SixMonthTotal,
count(case when o.OrderDate >= DATEADD(month, -12, GETDATE()) then 1 end) as TwelveMonthCount,
sum(case when o.OrderDate >= DATEADD(month, -12, GETDATE()) then BusinessVolumeTotal end) as TwelveMonthTotal
from (
select *,
ROW_NUMBER() over (partition by o.CustomerID order by OrderDate) as rn,
LEAD(OrderID) over (partition by o.CustomerID order by OrderDate) as nx
from Orders o
where o.OrderStatusID >= 7
and o.OrderTypeID in (1,4,8,11)
and o.OrderDate >= #timeAgo
) o
left join PriceTypes opt on opt.PriceTypeID = o.PriceTypeID
join (
select *,
ROW_NUMBER() over (partition by od.OrderID order by od.BusinessVolume desc) as rn
from OrderDetails od
) od on od.OrderID = o.OrderId
where rn = 1 or nx is null
) o on o.CustomerID = c.CustomerID
where c.CustomerStatusID in (1,2)
and c.CustomerTypeID in (2,3);
Good indexing is essential to good performance. I would expect roughly the following indexes on your tables, either clustered or non-clustered (clustered indexed INCLUDE every other column automatically), you can obviously add other INCLUDE columns if needed:
Customers (CustomerID) INCLUDE (FirstName, LastName)
CustomerTypes (CustomerTypeID) INCLUDE (CustomerTypeDescription)
CustomerStatuses (CustomerStatusID) INCLUDE (CustomerTypeDescription)
PeriodVolumes (CustomerID) INCLUDE (Volume80)
Periods (PeriodTypeID, StartDate, PeriodID) INCLUDE (EndDate) -- can swap Start and End
Orders (CustomerID, OrderDate) INCLUDE (OrderStatusID, SubTotal, Country, BusinessVolumeTotal)
OrderDetails (OrderID, BusinessVolume) INCLUDE (ItemCode ItemDescription)
PriceTypes (PriceTypeID) INCLUDE (PriceTypeDescription)
You should think carefully about INNER vs LEFT joins, because the optimizer can more easily move around an INNER join.
Note also, that DISTINCT is not a function, it is calculated over an entire set of columns. Generally, one can assume that if a DISTINCT is in the query then the joins have not been thought through properly.

sql join not taking all records from another table

I have a query like this
WITH CTE AS
(
SELECT
U.Name, U.Adluserid AS 'Empid',
MIN(CASE WHEN IOType = 0 THEN Edatetime END) AS 'IN',
MAX(CASE WHEN IOType = 1 THEN Edatetime END) AS 'out',
(CASE
WHEN MAX(E.Status) = 1 THEN 'AL'
WHEN MAX(E.Status) = 2 THEN 'SL'
ELSE 'L'
END) AS leave_status
FROM
Mx_ACSEventTrn
RIGHT JOIN
Mx_UserMst U ON Mx_ACSEventTrn.UsrRefcode = U.UserID
LEFT JOIN
Tbl_Zeo_Empstatus E ON Mx_ACSEventTrn.UsrRefcode = E.Emp_Id
WHERE
CAST(Edatetime AS DATE) BETWEEN '2019-11-03' AND '2019-11-03'
GROUP BY
U.Name, U.Adluserid
)
SELECT
[Name], [Empid], [IN], [OUT],
(CASE
WHEN CAST([IN] AS TIME) IS NULL THEN CAST(leave_status AS NVARCHAR(50))
WHEN CAST([IN] AS TIME) < CAST('08:15' AS TIME) THEN 'P'
ELSE 'L'
END) AS status
FROM
CTE
In my employee master table Mx_UserMst I have 67 employees. But here it is showing only a few employees those who are punched. I want to show all employees from employee master
I believe that the problem is his WHERE clause:
where cast(Edatetime as date) between '2019-11-03' and '2019-11-03'
Why not cast(Edatetime as date) = '2019-11-03'?
I'm not sure in which table the column Edatetime belongs (you should qualify all the columns with the correct table name/alias).
You must move the condition to an ON clause:
WITH CTE AS
(
select U.Name,U.Adluserid as 'Empid',
min(case when IOType=0 then Edatetime end) as 'IN',
max(case when IOType=1 then Edatetime end) as 'out',
case max(E.Status) when 1 then 'AL' when 2 then 'SL' else 'L' end as leave_status
from Mx_UserMst U
left join Mx_ACSEventTrn on Mx_ACSEventTrn.UsrRefcode=U.UserID and (cast(Edatetime as date) between '2019-11-03' and '2019-11-03')
left join Tbl_Zeo_Empstatus E on Mx_ACSEventTrn.UsrRefcode=E.Emp_Id
group by U.Name,U.Adluserid
)
SELECT [Name], [Empid],[IN],[OUT],
case
when cast([IN] as time) is null then cast(leave_status as nvarchar(50))
when cast([IN] as time) < cast('08:15' as time) then 'P'
else 'L'
end as status
FROM CTE
If Edatetime belongs to Tbl_Zeo_Empstatus move the condition to the next join's ON clause.
I also changed the RIGHT to a LEFT join so to make the statement more readable.
If you want to keep everything in a particular table, then that should be the first table in the FROM clause. Subsequent joins should be LEFT JOINs and conditions on subsequent tables should be in the ON clause rather than the WHERE clause.
I would also advise you to use table aliases and to only use single quotes for string and date constants -- NOT column aliases.
The following assumes that IOType and Edatetime are in the table Mx_ACSEventTrn. I should not have to guess. You should qualify all column names in the query.
WITH CTE AS (
SELECT U.Name, U.Adluserid AS Empid,
MIN(CASE WHEN AE.IOType = 0 THEN AE.Edatetime END) AS in_dt,
MAX(CASE WHEN AE.IOType = 1 THEN AE.Edatetime END) AS out_dt,
(CASE WHEN MAX(ES.Status) = 1 THEN 'AL'
WHEN MAX(ES.Status) = 2 THEN 'SL'
ELSE 'L'
END) AS leave_status
FROM Mx_UserMst U LEFT JOIN
Mx_ACSEventTrn AE
ON AE.UsrRefcode = U.UserID AND
CAST(AE.Edatetime AS DATE) BETWEEN '2019-11-03' AND '2019-11-03' LEFT JOIN
Tbl_Zeo_Empstatus ES
ON AE.UsrRefcode = ES.Emp_Id AND
GROUP BY U.Name, U.Adluserid
)
SELECT Name, Empid, IN_DT, OUT_DT,
(CASE WHEN IN_DT IS NULL THEN leave_status
WHEN CAST(IN_DT AS TIME) < CAST('08:15' AS TIME) THEN 'P'
ELSE 'L'
END) AS status
FROM CTE;
Some more points:
Don't name aliases things like IN that are already key words. That is why I gave it the name IN_DT.
There is no reason to cast to a TIME to compare to NULL.
I don't see a reason to cast to NVARCHAR(50) in the outer CASE expression.

Incorrect results using parameter in SSRS

I have an SSRS report for which I created two datasets for. One for the actual report and one for the parameter.
Dataset 1 is the report
Dataset 2 simply pulls the parameter that I want to report on from the table where it is stored.
When I run the report and select the parameter, it shows me all the values, but when I select one, I still get every record rather than just the records based on the parameter.
Dataset 1
SELECT stk.ItemCode AS ItemCode, stk.warehouse AS warehouse,
stk.Description AS Description, stk.Assortment AS Assortment,
Items.Class_02, stk.ItemGroup AS ItemGroup, stk.ItemStatus AS ItemStatus,
stk.ItemUnit AS ItemUnit, ISNULL((SELECT TOP 1 dbo.bacoSalesPrice(p.ID,
0) FROM staffl p WHERE ({d '2019-05-27'} BETWEEN validfrom AND
ISNULL(validto,{d '9999-12-31'}))
AND prijslijst = 'SALESPRICE' AND Items.ItemCode = artcode ORDER BY
artcode, validfrom desc),0) AS SalesPackagePrice,
ROUND((stk.Stock/stk.SalesPkg),2) AS Stock, ROUND((stk.Stock +
stk.QtyToBeReceived - stk.QtyToBeDelivered)/stk.SalesPkg,2) AS
AvailableStock, stk.SearchCode AS SearchCode FROM Items INNER JOIN (
SELECT
v.magcode AS warehouse,
MAX(i.itemcode) AS ItemCode,
MAX(i.description_0) AS Description,
i.SearchCode AS SearchCode,
MAX(ia.Assortment) AS Assortment,
MAX(ia.Description_0) AS ItemGroup,
MAX(CASE
WHEN i.condition = 'A' THEN 'Active'
WHEN i.condition = 'B' THEN 'Blocked'
WHEN i.condition = 'D' THEN 'Discontinued'
WHEN i.condition = 'E' THEN 'Inactive'
WHEN i.condition = 'F' THEN 'Future'
END) AS ItemStatus,
ISNULL(MAX(a.purchasepackage), '-') AS ItemUnit,
MAX(i.SalesPackagePrice) AS SalesPackagePrice,
MAX(v.bestniv) AS Minimum,
MAX(v.maxvrd) AS Maximum,
MAX(i.lev_crdnr) AS Supplier,
MAX(a.DeliveryTimeInDays) AS DeliveryTime,
MAX(ISNULL(a.SlsPkgsPerPurPkg,1)) AS SalesPkg,
ISNULL(Actual.Quantity,0) AS Stock
,ISNULL(SUM(QtyToReceived),0) AS QtyToBeReceived
,ISNULL(SUM(QtyToBeDelivered),0) AS QtyToBeDelivered
FROM items i
JOIN voorrd v ON i.itemcode=v.artcode
LEFT JOIN itemaccounts a ON i.itemcode=a.itemcode and i.lev_crdnr=a.crdnr
AND i.itemcode IS NOT NULL AND a.itemcode IS NOT NULL AND i.lev_crdnr IS N
NOT NULL AND a.crdnr IS NOT NULL
JOIN ItemAssortment ia ON ia.Assortment = i.Assortment
LEFT OUTER JOIN
dbo.TempToBeReceivedDelivered#3E5BBBE6#FB7B#4309#9CE1#560885B9BF94# budget
ON v.magcode = budget.warehouse AND i.ItemCode = budget.artcode
AND v.magcode IS NOT NULL AND budget.warehouse IS NOT NULL AND i.ItemCode IS NOT NULL AND budget.artcode IS NOT NULL
LEFT OUTER JOIN
(SELECT GX.artcode, GX.warehouse,
GX.Quantity,
GX.AmtActualStock, GX.TotalCnt,
CASE WHEN GX.FreeQuantity > GX.CurrentQuantity THEN (CASE WHEN GX.CurrentQuantity < 0 THEN 0 ELSE GX.CurrentQuantity END) ELSE
(CASE WHEN GX.FreeQuantity < 0 THEN 0 ELSE GX.FreeQuantity END) END As FreeQuantity
FROM (
SELECT sb.ItemCode AS artcode, SUM(CASE WHEN sb.Date <= {d '2019-05-27'} THEN sb.Quantity END) as Quantity,
ROUND(SUM(CASE WHEN sb.Date <= {d '2019-05-27'} THEN sb.StockAmount END),2) as AmtActualStock,
SUM(CASE WHEN sb.Date <= {d '2019-05-27'} THEN sb.Quantity END) as CurrentQuantity,
SUM(CASE WHEN sb.Date <= {d '2019-05-27'} THEN sb.FreeStock END) AS FreeQuantity, sb.Warehouse,
SUM(sb.GbkmutCount) AS TotalCnt
FROM StockBalances sb WITH (NOLOCK) JOIN Items ON sb.ItemCode = Items.ItemCode JOIN ItemAssortment ON Items.Assortment = ItemAssortment.Assortment
--WHERE Items.Class_02 in #Customer
GROUP BY sb.ItemCode, sb.WareHouse, Items.Class_02
HAVING SUM(sb.GbkmutCount) > 0) GX) AS Actual
ON Actual.artcode = i.ItemCode AND Actual.warehouse = v.magcode AND Actual.artcode IS NOT NULL AND i.ItemCode IS NOT NULL AND Actual.warehouse IS NOT NULL AND v.magcode IS NOT NULL
WHERE i.type IN ('S', 'B')
AND i.ItemCode BETWEEN '0030122186' AND 'XS45000'
AND i.Condition IN ('A')
AND ((i.IsSalesItem <> 0)) AND i.Type IN ('S', 'B')
AND 1=1
GROUP BY v.magcode , i.itemcode, i.SearchCode, Actual.Quantity
) stk ON items.ItemCode = stk.ItemCode
INNER JOIN grtbk ON Items.GLAccountDistribution = grtbk.reknr
ORDER BY stk.ItemCode
Dataset 2
SELECT Class_02, ItemCode FROM Items
Parameter in SSRS is set to Get values from Query, Value field: Class_02
Label field: Class_02

Using an array in conditional summation with grouping

I have the sql query thats work fine
SELECT
CAST(L.CreationUtcDateTime AS DATE),
SUM(L.Profit),
SUM(CASE WHEN (L.Network = 0 OR L.Network = 1) THEN L.Profit ELSE 0 END),
SUM(CASE WHEN (L.Network != 0 AND L.Network != 1) THEN L.Profit ELSE 0 END)
FROM
[dbo].[Leads] L WITH (NOLOCK)
LEFT JOIN [dbo].[Transactions] S WITH (NOLOCK) ON L.TransactionId = S.Id
GROUP BY
CAST(L.CreationUtcDateTime AS DATE);
I need to make it more generic by using variables instead of hard-coded constants.
I changed the query to:
DECLARE #matchNetworks TABLE (id int)
INSERT #matchNetworks(id) VALUES (0),(1)
SELECT
CAST(L.CreationUtcDateTime AS DATE),
SUM(L.Profit),
SUM(CASE WHEN (L.Network in (SELECT ID from #matchNetworks)) THEN L.Profit ELSE 0 END),
SUM(CASE WHEN (L.Network not in (SELECT ID from #matchNetworks)) THEN L.Profit ELSE 0 END)
FROM
[dbo].[Leads] L WITH (NOLOCK)
LEFT JOIN [dbo].[Transactions] S WITH (NOLOCK) ON L.TransactionId = S.Id
GROUP BY
CAST(L.CreationUtcDateTime AS DATE);
And now i have an error:
Cannot perform an aggregate function on an expression containing an aggregate or a subquery.
How can I use the predefined array of network ids in my query to avoid an error?
Move the comparison to the FROM clause using a LEFT JOIN:
SELECT CAST(L.CreationUtcDateTime AS DATE),
SUM(L.Profit),
SUM(CASE WHEN mn.ID IS NOT NULL THEN L.Profit ELSE 0 END),
SUM(CASE WHEN mn.ID IS NULL THEN L.Profit ELSE 0 END)
FROM [dbo].[Leads] L LEFT JOIN
[dbo].[Transactions] S
ON L.TransactionId = S.Id LEFT JOIN
#matchNetworks mn
ON mn.ID = l.Network
GROUP BY CAST(L.CreationUtcDateTime AS DATE);

Two Queries that should return similar results, SQL

Running on SQL Server 2005
Having a little trouble with 2 queries. The first returns a table like this:
NAME PAstDUe DueTomorr Due Today Due MONd Due Beyond
CLIENT 23 10 8 13 32
And the second returns a list of the same info that when i run in a pivot table in excel does not add up to the same table/data. Im using these for two different data sources/purposes for a project in visual studio so i cant just not use one. I dont see whats going wrong but i am calculation the results in different ways so i dont know if my math is off or something like that. This is really important because i need this data to be accurate on a day to day basis. If you wonder whats dbo.TruncateDate is doing, it refers the ordernumber of an order in our system and when ran with something involving a date this makes sure that portion of the query ignores all weekends and holidays based on a table in our system containing all of these dates. Hope that made sense. Let me know if i can provide more info.
Query1:
with cte AS (SELECT cl.Name,
SUM(CASE WHEN CURRENT_TIMESTAMP > oi.RequiredByDate THEN 1 ELSE 0 END) as PastDue
,SUM(CASE WHEN DATEADD(dd, DATEDIFF(dd, 0, oi.RequiredByDate), 0) = dateadd(day, datediff(day, '19000101',CURRENT_TIMESTAMP),'19000102') then 1 ELSE 0 END) as DueTomorrow
,SUM(CASE WHEN dbo.TruncateDate(CURRENT_TIMESTAMP) = dbo.TruncateDate(oi.RequiredByDate) THEN 1 Else 0 END) as DueToday
,SUM(CASE WHEN DateDiff(day, getdate(), RequiredByDate) BETWEEN 2 and 7 AND DateName(weekday, RequiredByDate) = 'Monday' Then 1 ELSE 0 END) as DueMonday
,SUM(CASE WHEN DATEADD(DAY, 2,dbo.TruncateDate(CURRENT_TIMESTAMP)) <= dbo.TruncateDate(oi.RequiredByDate) THEN 1 ELSE 0 END) as DueBeyond
FROM OrderItems oi
JOIN Orders o ON o.OrderID = oi.OrderID
JOIN Counties c ON c.FIPS = o.FIPS
JOIN Clients cl ON cl.ClientID = o.ClientID
JOIN Milestones m ON m.MilestoneID = oi.LastMilestoneID
JOIN Products p ON p.ProductID = oi.ProductID
JOIN Vendors v ON v.VendorID = oi.VendorID
LEFT JOIN ClientBranches clb ON clb.ClientID = o.ClientID
WHERE QueueID > 0 AND cl.Name NOT LIKE 'TES%'
AND cl.NAME LIKE 'HLC%'
GROUP BY cl.Name)
Select * FROM cte
Query 2:
SELECT cl.Name as Client, clb.Name as ClientBranch
,
CASE
WHEN CURRENT_TIMESTAMP > oi.RequiredByDate THEN 'Past Due'
WHEN dbo.TruncateDate(CURRENT_TIMESTAMP) = dbo.TruncateDate (oi.RequiredByDate) THEN 'Due Today'
WHEN DATEADD(dd, DATEDIFF(dd, 0, oi.RequiredByDate), 0) = dateadd(day, datediff(day, '19000101',CURRENT_TIMESTAMP),'19000102') then 'Due Tomorrow'
WHEN DateDiff(day, getdate(), RequiredByDate) BETWEEN 2 and 7 AND DateName(weekday, RequiredByDate) = 'Monday' Then 'Due Monday'
WHEN DATEADD(DAY, 2,dbo.TruncateDate(CURRENT_TIMESTAMP)) <= dbo.TruncateDate(oi.RequiredByDate) THEN 'Due Beyond'
ELSE 'WRONG' end
as DeliveryStatus
FROM OrderItems oi
JOIN Orders o ON o.OrderID = oi.OrderID
JOIN Counties c ON c.FIPS = o.FIPS
JOIN Clients cl ON cl.ClientID = o.ClientID
JOIN Milestones m ON m.MilestoneID = oi.LastMilestoneID
JOIN Products p ON p.ProductID = oi.ProductID
JOIN Vendors v ON v.VendorID = oi.VendorID
LEFT JOIN ClientBranches clb ON clb.ClientID = o.ClientID
WHERE QueueID > 0
and cl.Name not like ('Tes%')
and cl.Name Like 'HLC%'
Could you please run this:
with cte AS (SELECT cl.Name,
SUM(CASE WHEN CURRENT_TIMESTAMP > oi.RequiredByDate THEN 1 ELSE 0 END) as PastDue
,SUM(CASE WHEN DATEADD(dd, DATEDIFF(dd, 0, oi.RequiredByDate), 0) = dateadd(day, datediff(day, '19000101',CURRENT_TIMESTAMP),'19000102') then 1 ELSE 0 END) as DueTomorrow
,SUM(CASE WHEN dbo.TruncateDate(CURRENT_TIMESTAMP) = dbo.TruncateDate(oi.RequiredByDate) THEN 1 Else 0 END) as DueToday
,SUM(CASE WHEN DateDiff(day, getdate(), RequiredByDate) BETWEEN 2 and 7 AND DateName(weekday, RequiredByDate) = 'Monday' Then 1 ELSE 0 END) as DueMonday
,SUM(CASE WHEN DATEADD(DAY, 2,dbo.TruncateDate(CURRENT_TIMESTAMP)) <= dbo.TruncateDate(oi.RequiredByDate) THEN 1 ELSE 0 END) as DueBeyond,
COUNT(*) AS cnt
FROM OrderItems oi
JOIN Orders o ON o.OrderID = oi.OrderID
JOIN Counties c ON c.FIPS = o.FIPS
JOIN Clients cl ON cl.ClientID = o.ClientID
JOIN Milestones m ON m.MilestoneID = oi.LastMilestoneID
JOIN Products p ON p.ProductID = oi.ProductID
JOIN Vendors v ON v.VendorID = oi.VendorID
LEFT JOIN ClientBranches clb ON clb.ClientID = o.ClientID
WHERE QueueID > 0 AND cl.Name NOT LIKE 'TES%'
AND cl.NAME LIKE 'HLC%'
GROUP BY cl.Name)
and make sure that cnt is equal to the sum of all other fields?
Update:
Please run this
SELECT *
FROM (
SELECT cl.Name,
CASE WHEN CURRENT_TIMESTAMP > oi.RequiredByDate THEN 1 ELSE 0 END as PastDue,
CASE WHEN DATEADD(dd, DATEDIFF(dd, 0, oi.RequiredByDate), 0) = dateadd(day, datediff(day, '19000101',CURRENT_TIMESTAMP),'19000102') then 1 ELSE 0 END as DueTomorrow,
CASE WHEN dbo.TruncateDate(CURRENT_TIMESTAMP) = dbo.TruncateDate(oi.RequiredByDate) THEN 1 Else 0 END as DueToday,
CASE WHEN DateDiff(day, getdate(), RequiredByDate) BETWEEN 2 and 7 AND DateName(weekday, RequiredByDate) = 'Monday' Then 1 ELSE 0 END as DueMonday,
CASE WHEN DATEADD(DAY, 2,dbo.TruncateDate(CURRENT_TIMESTAMP)) <= dbo.TruncateDate(oi.RequiredByDate) THEN 1 ELSE 0 END as DueBeyond,
FROM OrderItems oi
JOIN Orders o ON o.OrderID = oi.OrderID
JOIN Counties c ON c.FIPS = o.FIPS
JOIN Clients cl ON cl.ClientID = o.ClientID
JOIN Milestones m ON m.MilestoneID = oi.LastMilestoneID
JOIN Products p ON p.ProductID = oi.ProductID
JOIN Vendors v ON v.VendorID = oi.VendorID
LEFT JOIN ClientBranches clb ON clb.ClientID = o.ClientID
WHERE QueueID > 0 AND cl.Name NOT LIKE 'TES%'
AND cl.NAME LIKE 'HLC%'
) q
WHERE PastDue + DueTomorrow + DueToday + DueMonday + DueBeyond > 1
and see the records which are counted more than once.