Count distinct in for xml path - sql

I am using the below query to get count of total number of employees that visited the outlet in each day of the month so the output will be like below
Outlet 12/01/2017 12/02/2017 -- rest days of the month
Outlet1 6 5
Outlet2 4 3
but the issue i have some duplicate values so i have to use count distinct
in the below query but it give the following error
Msg 156, Level 15, State 1, Line 1
Incorrect syntax near the keyword 'distinct'.
Note if i removed distinct the query works fine
Query
DECLARE #cols AS nvarchar(max),
#query AS nvarchar(max)
SELECT
#cols = STUFF((SELECT
',' + QUOTENAME(LogDate)
FROM dbo.AccessLog
WHERE month(CONVERT(datetime,LogDate)) = 12
and year(CONVERT(datetime,LogDate)) = 2017
and AccessLog.InOut = 0
GROUP BY LogDate
ORDER BY LogDate
FOR xml PATH (''), TYPE)
.value('.', 'NVARCHAR(MAX)'), 1, 1, '')
SET #query = 'SELECT abr as Outlet,' + #cols +
' from (select abr,LogDate,TerminalID,EmployeeID
from AccessLog
INNER JOIN dbo.Outlet
ON dbo.Outlet.Code = dbo.AccessLog.TerminalID
where AccessLog.InOut=0
and month(CONVERT(datetime,LogDate)) = ''12''
and year(CONVERT(datetime,LogDate)) = ''2017'') x
pivot (count(distinct EmployeeID)
for LogDate in (' + #cols + ') ) p '
EXECUTE (#query);

Related

show results in a Pivot way (no aggregate)

i've searched all over for an answer for this, and couldn't really find anything useful,
im pretty much a begginer in Sql so maybe i'm missing something basic
i have a simple table with 2 columns : line, fullname
this is the query i use :
SELECT
(select distributionline from extrasums where key=accounts.accountkey and SuFID='63') as 'line',
fullname
FROM
accounts
ORDER BY
(select distributionline from extrasums where key=accounts.accountkey and SuFID='63'),
(select dotinline from extrasums where key=accounts.accountkey and SuFID='68')
these are the results i get : (not allowed to embed images)
basically i get the distribution lines and the costumers, and the tables is ordered by the dist.lines and the place in line of each costumer.
all i want to do is show these results in a pivot-table style
i tried "pivot" but i understand you cant pivot without aggregate, because thats the whole point of "pivot" in sql.
this is what i want to achieve :
basically the dist.lines are the column names and the results are orders in a pivot way
the dist.lines are not permanent, one day we can have 3 lines. the other we can have 10 lines. its dynamic based on the deliveries for tomorrow. obviously.
same with the costumers.
you're welcome:
DROP TABLE IF EXISTS #temp;
SELECT RANK() OVER (PARTITION BY a.LINE ORDER BY a.fullname) AS rownum, a.line, a.fullname
INTO #temp
FROM ( SELECT 43 AS line, 'Daniel' AS fullname
UNION ALL
SELECT 43 AS line, 'john' AS fullname
UNION ALL
SELECT 43 AS line, 'kenny' AS fullname
UNION ALL
SELECT 43 AS line, 'adam' AS fullname
UNION ALL
SELECT 55 AS line, 'james' AS fullname
UNION ALL
SELECT 55 AS line, 'jones' AS fullname
UNION ALL
SELECT 68 AS line, 'kelly' AS fullname) AS a;
DECLARE #cols AS NVARCHAR(MAX), #colname AS NVARCHAR(MAX), #query AS NVARCHAR(MAX);
SET #colname = STUFF(( SELECT DISTINCT ',' + QUOTENAME(c.line)
FROM #temp AS c
FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)')
, 1
, 1
, '');
SET #cols = STUFF(( SELECT DISTINCT ',ISNULL(' + QUOTENAME(c.line) + ','''') AS '+QUOTENAME(c.line )
FROM #temp AS c
FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)')
, 1
, 1
, '');
PRINT #cols
PRINT #colname
SET #query =
N'SELECT rownum, ' + #cols
+ N' from
(
select rownum
, fullname
, line
from #temp
) x
pivot
(
max(fullname)
for line in (' + #colname + N')
) p ';
PRINT #query
EXECUTE (#query);
enter image description here

Getting the counts of the number of months a user has been using a particular service in SQL

I have data like below:
user_id month_id service
895 201612 S
262 201612 V
5300 201612 BB
Now there can be users who have used more than one service in a year, and I would like to have a query which gives me that. For example say 895 has used S for 3 months and BB for 4 months and then his latest service is V. So :
user_id S BB V
895 3 4 5
How do I do this pivot and count in SQL , can someone please help me?
Here is how you would do it dynamically:
DECLARE #Cols AS NVARCHAR(MAX)
,#Query AS NVARCHAR(MAX);
SET #Cols = STUFF((
SELECT DISTINCT ',' + QUOTENAME(service)
FROM YourTable
FOR XML PATH('')
,TYPE
).value('.', 'NVARCHAR(MAX)'), 1, 1, '')
SET #Query = 'SELECT user_id, ' + #Cols + ' from
(
select user_id
, month_id
, service
from YourTable
) x
pivot
(
COUNT(month_id)
for service in (' + #Cols + ')
) p '
EXECUTE(#Query)
Edit: Didn't realize it was a COUNT of month_id and not DATEDIFF(mm, CONVERT(DATETIME, month_id + ''01'',GETDATE()). Updated.

SQL Pivot - Dynamic Columns, No Aggregation

I'm trying to do a Pivot, but I'm not very experienced with pivots and I'm stuck - I can't figure out how to structure the query.
What I have:
Data Types (types of measurements that are recorded)
Locations
Data Sources (things at each location that will be measured)
Data Readings (measurements of the sources)
Additional information:
The number of Sources at any one Location can change
There will never be more than 5 sources at a single Location
Only 1 Reading is saved per Source/Type/date
In the returned table:
Table shows Data_Type info and Readings for a single Location and date
Columns: Data_Name, Units, Is_Required (from Data_Type table), plus one column for each Source
Rows: one row for each Data_Type
Rows should be ordered by Type_Display_Order
Sources (extra columns) should be ordered by Source_Display_Order
Some Readings are optional, and some Sources aren't measured daily - these still need to be included in the table
Example:
Table: Data_Type
Data_Type_ID Data_Name Units Is_Required (BIT) Type_Display_Order
-----------------------------------------------------------------------
1 Height In. 1 2
2 Length In. 0 3
3 Weight Lbs. 1 1
Table: Location
Location_ID Location
-----------------------
1 West
2 East
Table: Data_Source
Data_Source_ID Location_ID Source_Name Source_Display_Order
----------------------------------------------------------------
1 1 WCS 2
2 2 ECS 1
3 1 WBN 1
Table: Data_Reading
Data_Reading_ID Data_Type_ID Data_Source_ID Reading Reading_Date
----------------------------------------------------------------------
1 1 1 5 6/3/2016
2 3 2 3 5/1/2016
3 1 1 7 5/1/2016
4 2 3 2 6/3/2016
5 3 1 4 6/3/2016
Desired results from query for Location = "West", Date = 6/3/2016:
Data_Type_ID Data_Name Units Is_Required WBN WCS
---------------------------------------------------------
3 Weight Lbs. 1 NULL 4
1 Height In. 1 NULL 5
2 Length In. 0 NULL NULL
This solution seems to be similar: Pivot Dynamic Columns, no Aggregation but I'm still having some problems.
This is what I have so far:
DECLARE #date DATE, #locationID INT
SET #date = CAST('6/3/2016' AS DATE)
SET #locationID = 1
DECLARE #cols AS NVARCHAR(MAX), #query AS NVARCHAR(MAX)
SET #cols = STUFF((SELECT distinct ',' + QUOTENAME(s.Source_Name)
FROM Data_Source s
WHERE s.Location_ID = #locationID
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
SET #query = 'SELECT Data_Type_ID, Data_Name, Units, Is_Required, ' + #cols +
' FROM
(
SELECT
t.Data_Type_ID
, t.Data_Name
, t.Units
, t.Is_Required
, r.Reading
, s.Source_Name
FROM
Data_Type t
LEFT JOIN
Data_Reading r ON t.Data_Type_ID = r.Data_Type_ID
LEFT JOIN
Data_Source s ON r.Data_Source_ID = s.Data_Source_ID
WHERE
r.Reading_Date = CAST(CAST(' + #date + ' AS NVARCHAR(10)) AS DATE)
AND s.Location_ID = CAST(' + #locationID + ' AS INT)
) x
PIVOT
(
MIN(Reading)
for Source_Name in (' + #cols + ')
) p '
I have the query working properly now, but I still have a few problems:
#cols is not sorted by Source_Display_Order
rows are not sorted by Type_Display_Order (I did have ORDER BY in the inner SELECT statement for part X, but I was getting errors saying I can't have an ORDER BY clause there)
Date comparison in WHERE statement doesn't work - for some reason, it always computes as False, even when the dates are the same
Solved!
DECLARE #date DATE, #locationID INT
SET #date = CAST('6/3/2016' AS DATE)
SET #locationID = 1
DECLARE #cols AS NVARCHAR(MAX), #query AS NVARCHAR(MAX)
SET #cols = STUFF((SELECT ',' + QUOTENAME(s.Source_Name)
FROM Data_Source s
WHERE s.Location_ID = #locationID
ORDER BY s.Source_Display_Order
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
SET #query =
'SELECT
Data_Type_ID
, Data_Name
, Units
, Is_Required
, ' + #cols + '
FROM
(
SELECT
t.Data_Type_ID
, t.Data_Name
, t.Units
, t.Is_Required
, r.Reading
, s.Source_Name
, t.Type_Display_Order
FROM
Data_Type t
LEFT JOIN
Data_Reading r ON t.Data_Type_ID = r.Data_Type_ID
LEFT JOIN
Data_Source s ON r.Data_Source_ID = s.Data_Source_ID
WHERE
r.Reading_Date = ''' + CAST(#date AS NVARCHAR(10)) + '''
AND s.Location_ID = ' + CAST(#locationID AS NVARCHAR(20)) + '
) x
PIVOT
(
MIN(Reading)
for Source_Name in (' + #cols + ')
) p
ORDER BY
Type_Display_Order'
EXECUTE(#query)
To fix my problems:
Convert #date to NVARCHAR before adding to #query string and include extra quotes to surround the new NVARCHAR in quotes within #query
Remove DISTINCT clause from #cols and add ORDER BY (all of the names in my table are unique, so the DISTINCT is unnecessary)
Add Type_Display_Order to the inner SELECT statement, and add ORDER BY after the PIVOT statement

Invalid column name after PIVOT

I would like something like this:
42 | 41 | 31 | 32 | Name
----------------------------
O 42
X 41
P 32
Y 41
Z 41
The column headers that are pivoted are also the value in the name column. The various columns can have different statuses. This is what I have but I keep getting an error saying the ValveGroupName column is invalid.
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
select #cols = stuff((select ',' + quotename(ValveGroupName)
from dbo.adm_ValveGroup vgroup,
dbo.adm_Station station
where station.StationID = vgroup.StationID
and station.StationName in ('CBRN')
order by vgroup.ValveGroupName desc
for xml path(''), type).value('.', 'nvarchar(max)'), 1, 1, '')
set #query = 'select ValveGroupName,' + #cols + ' from
(
select vlog.status,
vgroup.ValveGroupName
from dbo.adm_Station station,
dbo.adm_ValveGroup vgroup,
dbo.valvegroup_log vlog
where station.StationID = vgroup.StationID
and vgroup.ValveGroupID = vlog.ValveGroupID
and station.StationName in (''CBRN'')
and vlog.logdate between ''2012-10-01'' and ''2012-10-30''
) x
pivot
(
max(status)
for ValveGroupName in (' + #cols + ')
) p '
execute (#query)
What's wrong with this query?
Since your ValveGroupName is becoming the new column names from the PIVOT, you normally won't include that in the final select list. Since you are basically "removing" the ValveGroupName to become the new columns, SQL Server doesn't have that column any longer so it throws an error.
The code would normally be:
set #query = 'select ' + #cols + '
from
(
select vlog.status,
vgroup.ValveGroupName
from dbo.adm_Station station
inner join dbo.adm_ValveGroup vgroup
on vgroup.ValveGroupID = vlog.ValveGroupID
inner join dbo.valvegroup_log vlog
on station.StationID = vgroup.StationID
where station.StationName in (''CBRN'')
and vlog.logdate between ''2012-10-01'' and ''2012-10-30''
) x
pivot
(
max(status)
for ValveGroupName in (' + #cols + ')
) p '
However, since you are aggregating a string that will return a single row for each ValveGroupName, if you want to return multiple rows, then you'll need to include a row_number:
set #query = 'select ' + #cols + '
from
(
select vlog.status,
vgroup.ValveGroupName,
seq = row_number() over(partition by vgroup.ValveGroupName
order by vlog.status)
from dbo.adm_Station station,
inner join dbo.adm_ValveGroup vgroup
on vgroup.ValveGroupID = vlog.ValveGroupID
inner join dbo.valvegroup_log vlog
on station.StationID = vgroup.StationID
where station.StationName in (''CBRN'')
and vlog.logdate between ''2012-10-01'' and ''2012-10-30''
) x
pivot
(
max(status)
for ValveGroupName in (' + #cols + ')
) p '
This change will allow you to return multiple rows in each column. Note: I also change the code to use INNER JOIN syntax instead of the comma joins, with the conditions in the WHERE clause.

Error in Dynamic PIVOT Table in SQL

I am getting error in Dynamic PIVOT Table.
I have a Tables like
TT_Child
TTChild_ID TT_ID Roll_No Std_Reg_ID Attendance
1 3 1 22 1
2 3 2 23 0
and Table
TT_Master
TT_ID Attend_date Time_from Time_To Course_ID Faculty_ID Acad_Year Subject_ID
1 2014-03-01 10:00 11:00 1 16 2013-2014 34
2 2014-03-02 10:00 11:00 1 16 2013-2014 34
3 2014-03-03 10:00 11:00 1 16 2013-2014 34
Student_Registration_Master
Std_Reg_ID Stud_FNAME stud_MNAME Stud_MNAME
--I am using this PIVOT query but getting this error. I have to use Attend_Date and Time as a Column Name.
Create PROCEDURE [dbo].[Attendance_Test]
#courseid as int=null, #acadyear nvarchar(15)=null
AS
Declare #colList varchar(max)
Declare #qry varchar(max)
SET #colList = STUFF((SELECT distinct ',' + QUOTENAME(CONVERT(VARCHAR(19), TTM.Attend_Date, 103)) + ' '+ QUOTENAME(TTM.Time_From)
FROM TT_Child SA
inner join TT_Master TTM on SA.TT_ID = TTM.TT_ID
where (TTM.Course_ID = #courseid)
FOR XML PATH(''), TYPE).value('/', 'NVARCHAR(MAX)') ,1,1,'')
SET #qry = 'SELECT SA.Reg_ID, STUD_FNAME + STUD_MNAME + STUD_LNAME as [Student Name], '+#colList+'
FROM
(
select SA.Reg_ID, SR.STUD_FNAME, SR.STUD_MNAME, SR.STUD_LNAME, TTM.Attend_Date , SA.Attendance from TT_Child SA
inner join TT_Master TTM on SA.TT_ID = TTM.TT_ID
inner join STUDENT_Registration_MASTER SR on CR.Reg_ID = SR.STUD_Reg_ID
where (TTM.Course_ID = '+cast(#courseid as varchar(50))+ ') and (TTM.Acad_Year = '''+#acadyear+''')
group by SA.Reg_ID, SR.STUD_FNAME, SR.STUD_MNAME, SR.STUD_LNAME, TTM.Attend_Date, SA.Attendance
) as s
PIVOT
(
MAX(Attendance) FOR Attend_Date IN (' + #colList + ')
) pvt'
print(#qry)
Exec(#qry)
-- I am getting this error message
SELECT SA.Reg_ID, STUD_FNAME + STUD_MNAME + STUD_LNAME as [Student Name], [01/03/2014] [10:00:00.0000000],[02/03/2014] [10:00:00.0000000],[03/03/2014] [10:00:00.0000000],[05/03/2014] [10:00:00.0000000],[05/03/2014] [11:00:00.0000000]
FROM
(
select SA.Reg_ID, SR.STUD_FNAME, SR.STUD_MNAME, SR.STUD_LNAME, TTM.Attend_Date , SA.Attendance from TT_Child SA
inner join TT_Master TTM on SA.TT_ID = TTM.TT_ID
inner join STUDENT_Registration_MASTER SR on CR.Reg_ID = SR.STUD_Reg_ID
where (TTM.Course_ID = 1) and (TTM.Acad_Year = '2013-2014')
group by SA.Reg_ID, SR.STUD_FNAME, SR.STUD_MNAME, SR.STUD_LNAME, TTM.Attend_Date, SA.Attendance
) as s
PIVOT
(
MAX(Attendance) FOR Attend_Date IN ([01/03/2014] [10:00:00.0000000],[02/03/2014] [10:00:00.0000000],[03/03/2014] [10:00:00.0000000],[05/03/2014] [10:00:00.0000000],[05/03/2014] [11:00:00.0000000])
) pvt
Error Message
Msg 102, Level 15, State 1, Line 12
Incorrect syntax near '10:00:00.0000000'.
Plz give solution
When you review this line of your code you will see the error:
MAX(Attendance)
FOR Attend_Date IN ([01/03/2014] [10:00:00.0000000],[02/03/2014] [10:00:00.0000000],
[03/03/2014] [10:00:00.0000000],[05/03/2014] [10:00:00.0000000],
[05/03/2014] [11:00:00.0000000])
The date and the time have each been wrapped in square brackets separately so you will get an error.
You need to concatenate the date and time together first, then wrap in square brackets using QUOTENAME. You should be able to change the code to:
SET #colList
= STUFF((SELECT distinct ',' + QUOTENAME(CONVERT(VARCHAR(19), TTM.Attend_Date, 103) + ' '+ TTM.Time_From)
FROM TT_Child SA
inner join TT_Master TTM on SA.TT_ID = TTM.TT_ID
where (TTM.Course_ID = #courseid)
FOR XML PATH(''), TYPE).value('/', 'NVARCHAR(MAX)') ,1,1,'')
See SQL Fiddle with Demo of your version and this new version.