Group By dynamic list with Count - sql-server

I'm using a simple case statement to count the occurrences of breakcode in my data and grouping by Seller and SaleDate.
However it seems like an inelegant solution. The breakcode could and quite possibly will expand and I would need to update my code when it does. How can this be done?
Additionally, while using NULL gives me the right answers, I would like to know is there a better approach to how I am counting my instances of breakcode, putting 0 in place of NULL returns the incorrect results?
SELECT seller,
saledate,
COUNT(CASE WHEN breakcode = 1 then 1 ELSE NULL END) as [Perfect],
COUNT(CASE WHEN breakcode = 2 then 1 ELSE NULL END) as [Simple],
COUNT(CASE WHEN breakcode = 3 then 1 ELSE NULL END) as [Medium],
COUNT(CASE WHEN breakcode = 4 then 1 ELSE NULL END) as [Dual1],
COUNT(CASE WHEN breakcode = 5 then 1 ELSE NULL END) as [Dual2],
COUNT(CASE WHEN breakcode = 6 then 1 ELSE NULL END) as [Hard],
COUNT(CASE WHEN breakcode = 7 then 1 ELSE NULL END) as [Difficult]
FROM test
GROUP BY seller, sale date
Thanks.
SQLFiddle: http://sqlfiddle.com/#!3/26f6d/2

Final version developed through comments:
DECLARE #sql AS NVARCHAR(MAX)
DECLARE #cols AS NVARCHAR(MAX)
SELECT #cols= ISNULL(#cols + ',','') + QUOTENAME(case when breakname like 'Perfect%' then 'Perfect' else breakname end)
FROM (select * from breaks where breakname not like 'Perfect - 90') a
group by id, breakname
order by id
SET #sql =
N'SELECT seller, saledate, ' + #cols + '
FROM (select seller, saledate, case when breakname like ''Perfect%'' then ''Perfect'' else breakname end breakname from test
inner join breaks on case when breakcode = 8 then 1 else breakcode end = id) derived
PIVOT(count(breakname)
FOR derived.breakname IN (' + #cols + ')) AS PVTTable
ORDER BY seller, saledate'
EXEC sp_executesql #sql
SQL Fiddle

Related

How to Mentioned Leavetype in employee attendance register?

I have two tables. One is employee attendance and second one is Employee Leave.
Employee attendance col( Empid,INTIME,OUTTIME,Status)
Employee Leave col(Empid,StartDate,Endate,leavetype(PL,CL,SL))
I have created pivot table in which present, absent and HD mentioned and created another separate query for sum of row (Present ,absent and HD) From EmployeeAttendance Table.
===Sum query===
SELECT SUM(CASE WHEN status = 'P' THEN 1
WHEN status = 'HD' THEN 0.5 WHEN status = 'A' THEN 0 END) AS [T.P],
SUM(CASE WHEN status = 'A' THEN 1 WHEN status = 'HD' THEN 0.5 END) AS [A],
SUM(CASE WHEN status = 'P' THEN 1
WHEN status = 'HD' THEN 1 WHEN status = 'A' THEN 1 END) AS [TDay ]
FROM EmployeesAttendance
--WHERE (ReportingDate BETWEEN #StartDate AND #Enddate)
GROUP BY EmpID
===Pivot Table ===
for converting col into row
SELECT DISTINCT ReportingDate INTO #Dates
FROM EmployeesAttendance
ORDER BY ReportingDate
DECLARE #cols NVARCHAR(4000)
SELECT #cols = COALESCE(#cols + ',[' + CONVERT(varchar, DATEPART(DAY, ReportingDate), 112)
+ ']','[' + CONVERT(varchar,DATEPART(DAY, ReportingDate), 112) + ']')
FROM #Dates
ORDER BY ReportingDate
DECLARE #qry NVARCHAR(4000) =
N'SELECT *
FROM (SElECT EmployeeDetails.EmpID,EmployeeDetails.EmpName,EmployeesAttendance.Status,
DATEPART(DAY, EmployeesAttendance.ReportingDate)as DDate
FROM EmployeesAttendance Inner Join EmployeeDetails on EmployeesAttendance.EmpID=EmployeeDetails.Empid )
emp
PIVOT (MAX(Status) FOR DDate IN (' + #cols + ')) AS stat
-- Executing the query
EXEC(#qry)
I want this Employee attendance Register.
Date 1 2 3 4 5 Total Absent Present Leave
Emp1 P P A PL P 5 1 3 1
Emp2 Cl pl A PL P 5 1 2 2
Now I want to merge above bother query of Sum and Pivot, also mentioned leave type in attendance register if employee availed leaves From 01-01-2019 to 02-01-2019 Noofdays equal to 2 for (emp2)

Simplifying Query to make it run faster

I have this extremely long winded query below. I am having issues with running it as it takes forever and keeps timing out on me:
with t as
(
select a.ID,
a.Date_Reported AS [Date Sent],
b.Date_Received AS [Date Returned],
(datediff(dd, a.date_reported, b.date_received)
+ CASE WHEN Datepart(dw, b.date_received) = 7 THEN 1 ELSE 0 END
- (Datediff(wk, a.date_reported, b.date_received) * 2 )
- CASE WHEN Datepart(dw, b.date_received) = 1 THEN 1 ELSE 0 END +
- CASE WHEN Datepart(dw, b.date_received) = 1 THEN 1 ELSE 0
END) AS [Overall_Time_Spent]
from [Transactions_External] a
join [Transactions] b on b.id like '%'+a.id+'%'
where a.customer = 'AA'
AND a.Date_Reported >= DATEADD(MONTH,-1,DATEADD(MONTH,DATEDIFF(MONTH,0,GETDATE()),0))
AND a.Date_Reported < DATEADD(d,1,EOMONTH(GETDATE(),-1))
AND a.ID IS NOT NULL
AND a.ID <> ''
AND b.ID not like '%_H'
)
select V.*
from
(
select
sum(case when Overall_Time_Spent < 0 then 1 else 0 end) as Errors,
sum(case when Overall_Time_Spent between 0 and 3 then 1 else 0 end) as _0_3_days,
sum(case when Overall_Time_Spent = 4 then 1 else 0 end) as _4_days,
sum(case when Overall_Time_Spent = 5 then 1 else 0 end) as _5_days,
sum(case when Overall_Time_Spent between 6 and 8 then 1 else 0 end) as _6_8_days,
sum(case when Overall_Time_Spent >= 9 then 1 else 0 end) as more_than_9_days,
count(Overall_Time_Spent) as Total
from t
) T1
cross apply
( values
('Count', convert(int, [Errors]), convert(int, [_0_3_days]), convert(int, [_4_days]), convert(int, [_5_days]), convert(int, [_6_8_days]), convert(int, [more_than_9_days]), convert(int, [Total]))
)
v([Time Taken (days)], [Errors], [0-3],[4],[5],[6-8],[9+], [Total])
The query is essentially looking at two tables, joining on id (which is slightly different on either table hence the like on the join) and then finding the difference in two dates to find the overall time spent. Then later on the times are split up into ranges. The query is restricted to last month only.
Any ideas what I can do to make this run faster or change the query about to help it run faster. I think the issue may be in the original select:
datediff(dd, a.date_reported, b.date_received)
+ CASE WHEN Datepart(dw, b.date_received) = 7 THEN 1 ELSE 0 END
- (Datediff(wk, a.date_reported, b.date_received) * 2 )
- CASE WHEN Datepart(dw, b.date_received) = 1 THEN 1 ELSE 0 END +
- CASE WHEN Datepart(dw, b.date_received) = 1 THEN 1 ELSE 0
END) AS [Overall_Time_Spent]
I may be selecting on all the database rather than last month?
one important thing to note is i am unable to create any tables or split the query up- so i really need to run selects and do it in one query. I am not sure this is possible.
join with like and "%" first is not recommended
join [Transactions] b on b.id like '%'+a.id+'%'
Index will not be used on a.id (if any) and it would require full scan. Maybe try to do an EXPLAIN of your query to see number of row scanned

SQL Server n Times Fixed Pivot

So, business people have asked me to display the data in a particular way.
They need to display X amount of columns for a particular value and if the value is not there, then fill it with zero.
In other words, what I have:
And what I need:
I dont know if I should approach this with a pivot or a CTE and a loop... can you help me?
Using SQL Server 2008.
You can use ROW_NUMBER and conditional aggregation to do this pivoting
WITH cte
AS (SELECT RECORD_ID,
NUMBER,
Row_number() OVER(PARTITION BY RECORD_ID ORDER BY NUMBER ) AS RN
FROM yourtable)
SELECT RECORD_ID,
COALESCE(Max(CASE WHEN rn = 1 THEN NUMBER END),0) AS NUMBER01,
COALESCE(Max(CASE WHEN rn = 2 THEN NUMBER END),0) AS NUMBER02,
COALESCE(Max(CASE WHEN rn = 3 THEN NUMBER END),0) AS NUMBER03,
COALESCE(Max(CASE WHEN rn = 4 THEN NUMBER END),0) AS NUMBER04,
COALESCE(Max(CASE WHEN rn = 5 THEN NUMBER END),0) AS NUMBER05,
...
COALESCE(Max(CASE WHEN rn = 10 THEN NUMBER END),0) AS NUMBER10
FROM CTE
GROUP BY RECORD_ID
Dynamic approach
DECLARE #x INT = 10, -- Replace it with required of columns
#intr INT = 1,
#col_list VARCHAR(8000)='',
#sql VARCHAR(max)=''
SET #sql = ' WITH cte
AS (SELECT RECORD_ID,
NUMBER,
Row_number() OVER(PARTITION BY RECORD_ID ORDER BY NUMBER ) AS RN
FROM yourtable)
SELECT RECORD_ID, '
/*nothing to worry about the below while loop it is just to frame the string*/
WHILE #intr <= #x
BEGIN
SET #col_list += 'COALESCE(Max(CASE WHEN rn = ' + Cast(#intr AS VARCHAR(50)) + ' THEN NUMBER END),0) AS NUMBER' + Cast(#intr AS VARCHAR(50)) + ','
SET #intr += 1
END
SET #col_list = LEFT(#col_list, Len(#col_list) - 1)
SET #sql += #col_list + ' FROM CTE
GROUP BY RECORD_ID '
EXEC ( #sql )

Count columns with a value x at least once

Suppose I have this table:
A B C
------------
1 0 0
0 0 1
1 0 1
0 0 0
I need the count of columns where 1 occurs (irrespective of the number of times it occurs). So in this example the count would be 2 since it occurs in 2 columns A & C.
How can this be done in SQL server?
Edit: From comments
Number of columns maybe fixed but unknown
A query like below will give you correct results if number of columns are known, for unknown number of columns this query can be made dynamic.
SELECT
MAX(CASE WHEN colA = 1 THEN 1 ELSE 0 END) +
MAX(CASE WHEN colB = 1 THEN 1 ELSE 0 END) +
MAX(CASE WHEN colC = 1 THEN 1 ELSE 0 END) +
--...
MAX(CASE WHEN colZ = 1 THEN 1 ELSE 0 END) as CountOfColumns
FROM tableT
Below is a dynamic query:
declare #q varchar(max)
select
#q= 'select ' +
stuff((
select
'+ MAX(CASE WHEN ' + C.name + ' = 1 THEN 1 ELSE 0 END) '
from
sys.columns C inner join sys.tables T
on C.object_id=T.object_id and T.name='tableT'
for xml path('')),1,1,'')
+ ' as CountOfColumns FROM tableT'
exec( #q)

Syntax error in EXEC(#sql) in stored procedure

I'm coding a sp to use in an asp.net page which displays a gridview (with multiple pages) and checkboxes for the select query. However it gives me syntax error on the EXEC(#sql), can't I call it within the if after using the WITH...AS ?
SET #sql=#sql +' ' +' FROM DATASET WHERE (RowId BETWEEN (#page - 1) * #pageSize AND (#page) * #pageSize)
OR (#page IS NULL)'
if #filter='pt'
BEGIN
WITH DataSet AS
(
SELECT TOP 100 PERCENT
row_number() over(order by date desc) as 'RowId'
, convert(varchar(10), date 105) AS 'date'
, product1
, product2
, product3
, product4
, Y
, N
FROM (select date
, count(case when product='product1' then 1 else null end) as 'product1'
, count(case when product='product2' then 1 else null end) as 'product1'
, count(case when product='product3' then 1 else null end) as 'product1'
, count(case when onsale='Y' then 1 else null end) as 'Y'
, count(case when onsale='N' then 1 else null end) as 'N'
from dbo.vwSales
group by date) as o
WHERE
(date BETWEEN #from AND #to)
)
EXEC (#sql)
END
You have to have the whole SQL in the variable for the Exec -command, starting with the "with ...".
Unless there is something we're not seeing here, there is no reason to execute dynamic SQL here. Writing dynamic SQL and executing it with EXEC is generally an anti-pattern, as it opens you up to the possibility of writing some inefficient code with security problems.
Can you just run...
WITH DataSet AS
(
SELECT TOP 100 PERCENT
row_number() over(order by date desc) as 'RowId'
, convert(varchar(10), date 105) AS 'date'
, product1
, product2
, product3
, product4
, Y
, N
FROM (select date
, count(case when product='product1' then 1 else null end) as 'product1'
, count(case when product='product2' then 1 else null end) as 'product1'
, count(case when product='product3' then 1 else null end) as 'product1'
, count(case when onsale='Y' then 1 else null end) as 'Y'
, count(case when onsale='N' then 1 else null end) as 'N'
from dbo.vwSales
group by date) as o
WHERE
(date BETWEEN #from AND #to)
)
FROM DATASET WHERE (RowId BETWEEN (#page - 1) * #pageSize AND (#page) * #pageSize)
OR (#page IS NULL)
Also note that the "TOP 100 PERCENT" is unnecessary.

Resources