Sql query for month wise - sql-server

I was trying on a sql query where we have search on month wise
for example my table is like
Username visited Visisted_Dated
A 1 01/11/2010
B 1 10/11/2010
A 1 03/12/2010
B 1 06/12/2010
B 1 06/12/2010
A 1 03/02/2011
B 1 05/02/2011
A 1 11/03/2011
A 1 20/03/2011
B 1 01/03/2011
Now if i want to search the users for no of visited between Feb to April i need to get output as
Users Nov_2010 Dec_2010 Jan_2011 Feb_2011 March_2011
A 1 1 0 1 2
B 1 2 0 1 1
Please let me know the way to proceed.
Thanks

I'm afraid you have to make use of dynamic SQL:
DECLARE #Sql VARCHAR(8000)
DECLARE #ColumnNames VARCHAR(1000)
DECLARE #BeginDate DATETIME
DECLARE #EndDate DATETIME
SET #BeginDate = '2010-11-1'
SET #EndDate = '2011-4-1'
SET #ColumnNames = ''
WHILE #BeginDate <= #EndDate
BEGIN
SET #ColumnNames = #ColumnNames + ',[' + DateName(month,#BeginDate) + '_' + Cast(Year(#BeginDate) AS VARCHAR(4)) + ']'
SET #BeginDate = DateAdd(Month, 1, #BeginDate)
END
IF Len(#ColumnNames) > 0
SET #ColumnNames = Right(#ColumnNames, Len(#ColumnNames) - 1)
PRINT #ColumnNames
SET #Sql = '
WITH U AS
(
SELECT UserName, DateName(month,Visited_Dated) + ''_'' + Cast(Year(Visited_Dated) AS VARCHAR(4)) AS VisitedMonth, Visited
FROM Users
)
SELECT *
FROM U
PIVOT (
SUM(Visited) FOR VisitedMonth IN (' + #ColumnNames + ')
) AS P'
EXEC (#Sql)

There it´s a solution based on PIVOT
SELECT UserName,[201011], [201012], [201101], [201102], [201103]
FROM
(SELECT UserName,Visited,convert(varchar(6),[Visited_Dated],112) Periodo
FROM [Table]) AS st
PIVOT
(
COUNT(Visited)
FOR Periodo IN ([201011], [201012], [201101], [201102], [201103])
) AS pt;

Related

Using pivot and then counting the values in each pivot column

I have a table that looks like this:
Agent_id
break_id
time
1
1
15
1
2
12
1
2
12
I used pivot to get this structure:
Agent_id
1
2
1
15
24
The problem is that I need to get the count for the pivoted columns, in the example I need to have a structure like this:
Agent_id
1
2
count1
count2
1
15
24
1
2
And I'm not sure on how to do it ... this is the query so far.
DECLARE #COLUMNS VARCHAR(MAX)
DECLARE #QUERY nVARCHAR(MAX)
SELECT #COLUMNS = COALESCE(#COLUMNS + ', ','') + QUOTENAME([break_id])
FROM
(SELECT DISTINCT [break_id] FROM test) AS B
ORDER BY B.[break_id]
SET #QUERY = '
SELECT agent_id,
'+#COLUMNS+'
FROM (
SELECT TOP (1000)
agent_id,break_id,time_inbreak
FROM test
) as pivotData
PIVOT (
SUM(time_inbreak)
FOR break_id IN ('+#COLUMNS+')
) as pivotResult
'
EXEC sp_executesql #QUERY
Any help is greatly appreciated
Unfortunately, PIVOT can only pivot a single column. But we can do multiple columns using conditional aggregation SUM(CASE WHEN... and COUNT(CASE WHEN...:
DECLARE #COLUMNS VARCHAR(MAX)
DECLARE #QUERY nVARCHAR(MAX)
SELECT #COLUMNS = COALESCE(#COLUMNS + ', ','') +
'Sum' + QUOTENAME([break_id]) +
' = SUM(CASE WHEN break_id = ' + break_id + ' THEN time_inbreak END), Count' +
QUOTENAME([break_id]) + ' = COUNT(CASE WHEN break_id = ' + break_id + ' THEN 1 END)'
FROM
(SELECT DISTINCT [break_id] FROM test) AS B
ORDER BY B.[break_id]
OPTION (MAXDOP 1);
SET #QUERY = '
SELECT agent_id,
'+#COLUMNS+'
FROM (
SELECT TOP (1000)
agent_id,break_id,time_inbreak
FROM test
) as pivotData
GROUP BY agent_id;
';
PRINT #QUERY
EXEC sp_executesql #QUERY
I must say, I'm not sure how safe it is to aggregate the columns like that, especially in the face of parallelism. Preferably use STRING_AGG or FOR XML PATH(''), TYPE. At the very least I have added OPTION (MAXDOP 1) to prevent parallelism

SQL convert rows generated by CTE to columns - including fieldnames

I have a CTE that generates data into a variable number of rows. These are hourly values that I need to use in a join to another table, but that part is not important here.
The CTE part alone looks like this:
DECLARE #interval INT = 3 -- hours
DECLARE #period INT = 24 -- hours
;WITH cteHour AS (
SELECT CAST('i0' AS VARCHAR(6)) AS fieldname, 0 AS i, 0 AS startpos
UNION ALL
SELECT CONCAT('i', CAST(i + 1 AS VARCHAR(5))) AS fieldname, i + 1 AS i, startpos + #interval AS startpos
FROM cteHour
WHERE (startpos + #interval) < #period
) SELECT fieldname, startpos FROM cteHour
And it generates data like this (depending on the #interval and #period values):
fieldname startpos
--------- -----------
i0 0
i1 3
i2 6
i3 9
i4 12
i5 15
i6 18
i7 21
My question is how can I get the results into columns instead of rows, using the first column as the field names, like this:
i0 i1 i2 i3 i4 i5 i6 i7
-- -- -- -- -- -- -- --
0 3 6 9 12 15 18 21
I'm guessing it will require a PIVOT, but as far as I know pivoting cannot generate field names dynamically. So I'm open to any answers that get the job done.
Since it required a dynamic sql, skip the pivot part and straight away generate the dynamic query from your cte
DECLARE #sql NVARCHAR(MAX)
SELECT #sql = isnull(#sql + ',', 'select ')
+ quotename(fieldname) + '=' + convert(varchar(10), startpos)
FROM cteHour
order by startpos
print #sql
exec sp_executesql #sql
The following query should do what you want, it uses a temp table but you'd get an idea on how the approach is
DECLARE #interval INT = 3 -- hours
DECLARE #period INT = 24 -- hours
;WITH cteHour AS (
SELECT CAST('i0' AS VARCHAR(6)) AS fieldname, 0 AS i, 0 AS startpos
UNION ALL
SELECT CONCAT('i', CAST(i + 1 AS VARCHAR(5))) AS fieldname, i + 1 AS i, startpos + #interval AS startpos
FROM cteHour
WHERE (startpos + #interval) < #period
)
SELECT * INTO #temp FROM cteHour;
DECLARE #pvt NVARCHAR(MAX) = '';
SET #pvt = STUFF(
(SELECT DISTINCT N', ' + QUOTENAME([fieldname]) FROM #temp FOR XML PATH('')),1,2,N'')
EXEC (N'SELECT *
FROM #temp
PIVOT (MAX([startpos]) FOR [fieldname] IN('+#pvt+')) AS PIV');
Here is the simplest answer using a dynamic query
DECLARE #interval INT = 3 -- hours
DECLARE #period INT = 24 -- hours
DECLARE #startpos INT = 0
DECLARE #i INT = 0
DECLARE #sql NVARCHAR(MAX) = 'SELECT '
WHILE #startpos < #period
BEGIN
SET #sql = #sql + (CASE WHEN #i > 0 THEN ',' ELSE '' END) +
CAST(#startpos AS VARCHAR(5)) + ' AS i' + CAST(#i AS VARCHAR(5))
SET #i = #i + 1
SET #startpos = #startpos + #interval
END
print #sql
exec sp_executesql #sql

Getting dynamic query select fields to show in Crystal Reports

I have what I consider to be a pretty complicated query (at least to me) and I decided to attempt to solve it using dynamic SQL. However I have two issues that I have not been able to solve.
Situation
In a table, a user can enter an item which has an amount, week and status.
So the data is supposed to resembles this format.
Week 1 Week 2
---------+-----------+-------------
Status 1 | 50 25
Status 2 10 20
And this is the data in SQL.
Status 1 | Week 1 | 25
Status 1 | Week 1 | 25
Status 1 | Week 2 | 25
Status 2 | Week 1 | 2
Status 2 | Week 1 | 8
Status 2 | Week 1 | 10
Status 2 | Week 1 | 10
For each status I sum the amount based on weeks using a dynamic pivot table.
What have I attempted:
--EXEC usp_weekReport #weeks=1, #year='2019'
ALTER PROCEDURE usp_weekReport
(#weeks INT,
#year NVARCHAR(4))
AS
DECLARE #columns NVARCHAR(MAX), #sql NVARCHAR(MAX);
SET #columns = N'';
SELECT #columns += N', p.' + QUOTENAME([week])
FROM
(SELECT p.[week]
FROM [dbo].[Invoices] P
WHERE DATEPART(YEAR, P.date) = #year
AND ([week] IN (#weeks)
OR [week] IN (#weeks + 1)
OR [week] IN (#weeks + 2)
OR [week] IN (#weeks + 3)
OR [week] IN (#weeks + 4)
OR [week] IN (#weeks + 5))
GROUP BY P.[week]) AS x;
SET #sql = N'
SELECT p.[statusName],' + STUFF(#columns, 1, 2, '') + '
FROM
(
SELECT
SUM(CAST(REPLACE(REPLACE(A.amount,'','',''''),''$'','''') AS FLOAT)) as sumInvoice,
A.invoiceStatusID_FK,
B.statusName,
[week]
FROM [dbo].[Invoices] A
INNER JOIN invoiceStatus B
ON A.invoiceStatusID_FK=B.invoiceStatusID
GROUP BY invoiceStatusID_FK,B.statusName,[week]--,C.programme
) AS j
PIVOT
(
SUM(sumInvoice) FOR [week] IN ('
+ STUFF(REPLACE(#columns, ', p.[', ',['), 1, 1, '')
+ ')
) AS p;';
--PRINT #sql;
--EXEC sp_executesql #sql;
CREATE TABLE #reportResult
(
statusName nvarchar(50),
weekA INT DEFAULT 0,
weekB int DEFAULT 0
--weekC int DEFAULT 0,
--weekD int DEFAULT 0,
--weekE int DEFAULT 0,
--weekF int DEFAULT 0
)
INSERT #reportResult Exec(#sql)
SELECT statusName, weekA,weekB--,weekC,weekD,weekE,weekF -- here you have "static SELECT with field names"
FROM #reportResult
DROP TABLE #reportResult
To solve this, I have the above code and while it works(returns the values in SQL) I have two issues.
Problems
My first problem is that, I cannot use this code in conjunction with creating a report in Crystal Reports. When I import the stored procedure, the columns shows up blank. I looked at the following link. [Select field Names from Dynamic SQL query][1]
[1]: SELECT fieldnames FROM dynamic SQL query and even if I attempted to model my answer after what was said to work exactly. It does not seem to work for me as my data columns are still blank in Crystal.
I had the idea of calling my first stored procedure from a separate stored procedure but given of how my answer is returned (not just a single value that I can assign to a variable, currently thinking of returning a table value function) I doubt that that would work.
A second problem that developed is because my 'weeks' are dynamic (up to 6 weeks) I cant create the temp table with "Spare" columns or I get an error (incorrect number of columns), as you see i commented them out and I also can't use "Select into"
Appreciate any assistance or ideas offered.
Try following, it uses Global TEMP table:
ALTER PROCEDURE usp_weekReport
#weeks INT,
#year NVARCHAR(4)
AS
BEGIN
DECLARE #columns NVARCHAR(MAX), #sql NVARCHAR(MAX);
SET #columns = N'';
SELECT #columns+=N', p.'+QUOTENAME([week])
FROM (
SELECT p.[week]
FROM [dbo].[Invoices] P
WHERE DATEPART(YEAR, P.date) = #year
AND ([week] IN(#weeks)
OR [week] IN(#weeks + 1)
OR [week] IN(#weeks + 2)
OR [week] IN(#weeks + 3)
OR [week] IN(#weeks + 4)
OR [week] IN(#weeks + 5))
GROUP BY P.[week]
) AS x;
SET #sql = N'
SELECT p.[statusName],'+STUFF(#columns, 1, 2, '')+'
into ##reportResult
FROM
(
SELECT
SUM(CAST(REPLACE(REPLACE(A.amount,'','',''''),''$'','''') AS FLOAT)) as sumInvoice,
A.invoiceStatusID_FK,
B.statusName,
[week]
FROM [dbo].[Invoices] A
INNER JOIN invoiceStatus B
ON A.invoiceStatusID_FK=B.invoiceStatusID
GROUP BY invoiceStatusID_FK,B.statusName,[week]--,C.programme
) AS j
PIVOT
(
SUM(sumInvoice) FOR [week] IN ('+STUFF(REPLACE(#columns, ', p.[', ',['), 1, 1, '')+')
) AS p;';
EXEC sp_executesql #sql;
SELECT *
FROM ##reportResult;
IF OBJECT_ID('tempdb..##reportResult') IS NOT NULL
BEGIN
DROP TABLE ##reportResult;
END;
END
Here is a solution without the Global temp table:
ALTER PROCEDURE usp_weekReport
#weeks INT,
#year NVARCHAR(4)
AS
BEGIN
DECLARE #columns NVARCHAR(MAX), #sql NVARCHAR(MAX), #COLCOUNT int, #TableColumns NVARCHAR(MAX);
SET #columns = N'';
SELECT #columns+=N', p.'+QUOTENAME([week])
FROM (
SELECT p.[week]
FROM [dbo].[Invoices] P
WHERE DATEPART(YEAR, P.date) = #year
AND ([week] IN(#weeks)
OR [week] IN(#weeks + 1)
OR [week] IN(#weeks + 2)
OR [week] IN(#weeks + 3)
OR [week] IN(#weeks + 4)
OR [week] IN(#weeks + 5))
GROUP BY P.[week]
) AS x;
SELECT #COLCOUNT = count(*)
FROM (
SELECT p.[week]
FROM [dbo].[Invoices] P
WHERE DATEPART(YEAR, P.date) = #year
AND ([week] IN(#weeks)
OR [week] IN(#weeks + 1)
OR [week] IN(#weeks + 2)
OR [week] IN(#weeks + 3)
OR [week] IN(#weeks + 4)
OR [week] IN(#weeks + 5))
GROUP BY P.[week]
) AS x;
SELECT #TableColumns = CASE When #COLCOUNT = 1 THEN 'weekA'
When #COLCOUNT = 2 THEN 'weekA, weekB'
When #COLCOUNT = 3 THEN 'weekA, weekB, weekC'
When #COLCOUNT = 4 THEN 'weekA, weekB, weekC, weekD'
When #COLCOUNT = 5 THEN 'weekA, weekB, weekC, weekD, weekE'
When #COLCOUNT = 6 THEN 'weekA, weekB, weekC, weekD, weekE, weekF'
end;
CREATE TABLE #reportResult
(
statusName nvarchar(50),
weekA INT DEFAULT 0,
weekB int DEFAULT 0,
weekC int DEFAULT 0,
weekD int DEFAULT 0,
weekE int DEFAULT 0,
weekF int DEFAULT 0
)
SET #sql = N'
INSERT INTO #reportResult (statusName,' + #TableColumns + ')
SELECT p.[statusName],'+STUFF(#columns, 1, 2, '')+'
into ##reportResult
FROM
(
SELECT
SUM(CAST(REPLACE(REPLACE(A.amount,'','',''''),''$'','''') AS FLOAT)) as sumInvoice,
A.invoiceStatusID_FK,
B.statusName,
[week]
FROM [dbo].[Invoices] A
INNER JOIN invoiceStatus B
ON A.invoiceStatusID_FK=B.invoiceStatusID
GROUP BY invoiceStatusID_FK,B.statusName,[week]--,C.programme
) AS j
PIVOT
(
SUM(sumInvoice) FOR [week] IN ('+STUFF(REPLACE(#columns, ', p.[', ',['), 1, 1, '')+')
) AS p;';
EXEC sp_executesql #sql;
SELECT *
FROM #reportResult;
IF OBJECT_ID('tempdb..#reportResult') IS NOT NULL
BEGIN
DROP TABLE #reportResult;
END;
END

Query to retrieve column values as rows from table which has duplicate rows in SQL Server 2008

I have a table which has records in the following format:
OrderID | CategoryID | Percentage
-----------------------------------------
Order1 1 50
Order1 2 100
Order1 3 70
Order2 1 85
Order2 2 75
Order2 3 100
Order3 1 50
Order3 2 60
Order3 3 80
Each order has multiple categories and each category has a percentage corresponding to that order.
I want to display it in the following format:
OrderID | Category1_% | Category2_% | Category3_%
-------------------------------------------------------
Order1 50 100 70
Order2 85 75 100
Order3 50 60 80
How do I write a query in SQL Server 2008 to retrieve percentages for each category of each (distinct) order? Please help.
Edit:
Orders table
ID | StartDate
--------------------
Order1 1/1/2016
Order2 10/1/2015
Order3 10/5/2016
The query which I executed is:
Declare #SQL varchar(max)
Declare #startDate datetime
set #startDate='1/1/2015'
Declare #endDate datetime
set #endDate= '10/1/2016'
Select #SQL = Stuff((Select Distinct ',' + QuoteName('Category'+cast(CategoryID as varchar(25))+'_%') From OrdersData Order By 1 For XML Path('')),1,1,'')
Select #SQL = 'Select [OrderID],' + #SQL +'
From (Select OrderID,Percentage,Item=''Category''+cast(CategoryID as varchar(25))+''_%'' from OrdersData join Orders on Orders.ID =OrdersData.OrderID
WHERE Orders.StartDate >= '+ ***convert(nvarchar(25), #startDate, 121)*** + ' and GCWR.StartDate <= '+ ***convert(nvarchar(25), #endDate, 121)*** +') A
Pivot (max(Percentage) For Item in (' + #SQL + ') ) p'
Exec(#SQL);
When I execute, I get an error 'Incorrect syntax near '00'.' How do I call the datetime values in the above query to display the results? Please let me know.
Just in case you need to go dynamic
Declare #SQL varchar(max)
Select #SQL = Stuff((Select Distinct ',' + QuoteName('Category'+cast(CategoryID as varchar(25))+'_%') From YourTable Order By 1 For XML Path('')),1,1,'')
Select #SQL = 'Select [OrderID],' + #SQL +'
From (Select OrderID,Percentage,Item=''Category''+cast(CategoryID as varchar(25))+''_%'' from YourTable) A
Pivot (max(Percentage) For Item in (' + #SQL + ') ) p'
Exec(#SQL);
Returns
Edit - Updated with StartDate
Declare #Date1 datetime = '2015-01-01'
Declare #Date2 datetime = '2016-10-01'
Declare #SQL varchar(max)
Select #SQL = Stuff((Select Distinct ',' + QuoteName('Category'+cast(CategoryID as varchar(25))+'_%') From YourTable Where cast(StartDate as date) between #Date1 and #Date2 Order By 1 For XML Path('')),1,1,'')
Select #SQL = 'Select [OrderID],' + #SQL +'
From (Select OrderID,Percentage,Item=''Category''+cast(CategoryID as varchar(25))+''_%'' from YourTable Where cast(StartDate as date) between '''+convert(varchar(10),#Date1,120)+''' and '''+convert(varchar(10),#Date2,120)+''') A
Pivot (max(Percentage) For Item in (' + #SQL + ') ) p'
Exec(#SQL);
Returns
Give this a try:
Select OrderID, [1] as [Category1_%], [2] as [Category2_%],[3] as [Category3_%]
From [TableName]
PIVOT (MAX([Percentage]) FOR [CategoryID] IN ([1],[2],[3])) myPivot
This assumes that you know all the possible values in the categoryID field and that each CategoryID an OrderID combination has a single percentage value.

how to take the corresponding value from sub query

I have a stored procedure like this:
ALTER PROCEDURE [dbo].[ParkingDeatailsReportnew]
#startdate NVARCHAR(100),
#enddate NVARCHAR(100)AS BEGINDECLARE #cols AS NVARCHAR(MAX) , #query AS NVARCHAR(MAX)
SELECT #cols = STUFF(( SELECT DISTINCT ',' + QUOTENAME(Vtype)
FROM dbo.VType_tbl FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)'), 1, 1, '')
SET #query =
'SELECT LocName,Date, ' + #cols + '
from (
select l.LocName,v.Vtype, convert(date, dtime) as Date
from Transaction_tbl t
join VType_tbl v on t.vtid = v.vtid
where dtime between ''' + #startdate + ''' and ''' + #enddate + '''
and locid IN (SELECT l.Locid FROM dbo.Location_tbl l)
) d pivot ( count(Vtype) for Vtype in (' + #cols + ') ) p ' EXEC sys.sp_executesql #query
END
I am getting out put like this:
Date Emaar Staff Lost Ticket Normal VIP VVIP
---------- ----------- ----------- ----------- ----------- -----------
2013-05-08 1 0 12 6 3
2013-05-09 0 0 1 0 0
I have one more table name location table.i want to show particular location name(locname) also in my output
expected output
Location Date Emaar Staff Lost Ticket Normal VIP VVIP
------- -- - ---------- ----------- ----------- ----------- ----------- -----------
Fasion 2013-05-08 1 0 12 6 3
blooimg 2013-05-09 0 0 1 0 0
If you want to see the location outputted, you need to include it in your select
'SELECT Location, Date, ' + #cols + '
from (
select Location, v.Vtype, convert(date, dtime) as Date
....

Resources