SQL Server Group By Help Required - sql-server

Being a beginner, how can I get results as mentioned in the diagram. I am not getting results by grouping. Please advise.

If I'm reading your example data correctly, it seems that for Query3, you have the data flip-flopped for the dates. I think Query3 elapsed for 10/20/2017 should be 2:03 and for 10/21/2017, the elapsed time should be 1:48.
If that is indeed the case, here is a solution that uses a combination of Common Table Expressions, aggregation and PIVOT. It works for the example data you've provided in your question. You may need to adjust it for other data.
set nocount on
Declare #t Table (ID int, [Date] Date, ExecutedTime varchar(10), Label Varchar(10))
insert into #t values
(1,'2017-10-20','0:01:16','Query1'),
(2,'2017-10-20','0:00:20','Query1'),
(3,'2017-10-20','0:00:14','Query1'),
(4,'2017-10-20','0:01:43','Query2'),
(5,'2017-10-20','0:00:33','Query2'),
(6,'2017-10-20','0:00:34','Query2'),
(7,'2017-10-20','0:01:18','Query3'),
(8,'2017-10-20','0:00:30','Query3'),
(9,'2017-10-20','0:00:15','Query3'),
(10,'2017-10-21','0:01:16','Query1'),
(11,'2017-10-21','0:00:20','Query1'),
(12,'2017-10-21','0:00:14','Query1'),
(13,'2017-10-21','0:01:43','Query2'),
(14,'2017-10-21','0:00:33','Query2'),
(15,'2017-10-21','0:00:34','Query2'),
(16,'2017-10-21','0:01:18','Query3'),
(16,'2017-10-21','0:00:30','Query3')
;
With ExecutedTimeInSeconds as --Convert ExecutedTime to seconds in preparation for aggregation
(
select
[Date],
Label,
datepart(hour,(convert(time,executedTime))) * 3600 +
datepart(minute,(convert(time,executedTime))) * 60 +
datepart(second,(convert(time,executedTime))) as ElapsedSeconds
from #t
)
,AggregatedDataInElapsedSeconds as --Aggregate the seconds by Date and Label
(
SELECT [Date]
,Label
,sum(ElapsedSeconds) AS ElapsedSeconds
FROM ExecutedTimeInSeconds
GROUP BY [Date]
,Label
),
DataReadyForPivot as --Convert the aggregageted seconds back to an elapsed time
(
SELECT [Date], Label,
RIGHT('0' + CAST(ElapsedSeconds / 3600 AS VARCHAR),2) + ':' +
RIGHT('0' + CAST((ElapsedSeconds / 60) % 60 AS VARCHAR),2) + ':' +
RIGHT('0' + CAST(ElapsedSeconds % 60 AS VARCHAR),2) as ExecutedSum
from AggregatedDataInElapsedSeconds
)
,
PivotedData as --Pivot the data
(
SELECT *
FROM DataReadyForPivot
PIVOT(MAX(ExecutedSum) FOR Label IN (
[Query1]
,[Query2]
,[Query3]
)) AS pvt
)
select --add a row number as Id
ROW_NUMBER() over (order by [Date] desc) as Id,
*
from PivotedData
| Id | Date | Query1 | Query2 | Query3 |
|----|------------|----------|----------|----------|
| 1 | 2017-10-21 | 00:01:50 | 00:02:50 | 00:01:48 |
| 2 | 2017-10-20 | 00:01:50 | 00:02:50 | 00:02:03 |

Related

Modify DATE in COLUMN using CASES and LOOP

I have One month data.
DATE | AMOUNT
04/01/2019 | 3437824
04/02/2019 | 234834
04/03/2019 | 2343478
.
.
.
04/30/2019 | 343729
I want to change the Date Column to give me something like this
OLDDATE | DATE | AMOUNT
04/01/2019 | 01/01/2019 | 3437824
04/02/2019 | 02/01/2019 | 234834
04/03/2019 | 03/01/2019 | 2343478
.
.
.
04/12/2019 | 12/01/2019 | 328456
04/13/2019 | 01/02/2019 |845754
04/14/2019 | 02/02/2019 |845754
.
.
04/24/2019 | 12/02/2019 |845754
04/25/2019 | 01/03/2019 |845754
04/26/2019 | 02/03/2019 |845754
.
.
.
04/30/2019 | 06/03/2019 | 343729
I tried to achieve the same using a switch case inside a while loop but was not able to get the desired result
BEGIN
DECLARE #Mth INT = 1;
DECLARE #Iteration INT = 1;
WHILE #Iteration != 1000000
BEGIN
WHILE #Mth <= 12
BEGIN
UPDATE TACC
SET BUSINESSDATE =
CASE
WHEN DAY([BUSINESSDATE])<13
THEN datefromparts(year([BUSINESSDATE]), #Mth, 1)
WHEN DAY([BUSINESSDATE]) BETWEEN 13 AND 24
THEN datefromparts(year([BUSINESSDATE]), #Mth, 2)
ELSE datefromparts(year([BUSINESSDATE]), #Mth, 3)
END
SET #Mth = #Mth + 1
IF #Mth=13
BEGIN
SET #Mth=1
END
END
SET #Iteration = #Iteration + 1
END
END
Which will be the better way to do it T-SQL or DAX if i have to do this for say 10 million rows
Basically, we need to divide the rows in groups of 12 and for each group to increase the month part of the new date. The rows in the each groups need to be ordered and we need to increase the day part of each new date with one.
You can use ROW_NUMBER to order the rows and divide them in groups. Then you can use DATEADD to built the new date for each row:
DECLARE #DataSource TABLE
(
[DATE] DATETIME2(0)
,[AMOUNT] INT
);
INSERT INTO #DataSource ([DATE], [AMOUNT])
VALUES ('2019-04-01', 100)
,('2019-04-02', 100)
,('2019-04-03', 100)
,('2019-04-04', 100)
,('2019-04-05', 100)
,('2019-04-06', 100)
,('2019-04-07', 100)
,('2019-04-08', 100)
,('2019-04-09', 100)
,('2019-04-10', 100)
,('2019-04-11', 100)
,('2019-04-12', 100)
,('2019-04-13', 100)
,('2019-04-14', 100)
,('2019-04-15', 100)
,('2019-04-16', 100)
,('2019-04-17', 100)
,('2019-04-18', 100)
,('2019-04-19', 100)
,('2019-04-20', 100)
,('2019-04-21', 100)
,('2019-04-22', 100)
,('2019-04-23', 100)
,('2019-04-24', 100)
,('2019-04-25', 100)
,('2019-04-26', 100)
,('2019-04-27', 100)
,('2019-04-28', 100)
,('2019-04-29', 100);
DECLARE #StartDate DATETIME2 = '2019-01-01'
,#Step TINYINT = 12;
WITH DataSource ([OLDDATE], [AMOUNT], [AddMonth]) AS
(
SELECT *
,(ROW_NUMBER() OVER (ORDER BY [Date] ASC) -1 ) / 12
FROM #DataSource
)
SELECT [OLDDATE]
,[AMOUNT]
,DATEADD(DAY, ROW_NUMBER() OVER (PARTITION BY [AddMonth] ORDER BY [OLDDATE] ASC) - 1, DATEADD(MONTH, [AddMonth], #StartDate))
FROM DataSource;

Automatically generate columns name in CTE using SQL Server

I am building a pivot query inside a CTE. I have a table Table_1:
Store Week xCount
------- ---- ------
101 1 138
105 1 37
109 1 59
101 2 282
109 2 97
105 3 60
109 3 87
This is the query I used to pivot Table_1:
with CTE as
(
select
*
from
(select
store, week, xCount
from
table_1) src
pivot
(sum(xcount)
for week in ([1], [2], [3])
) piv;
)
Select *
From CTE
And this is the result I got:
| STORE | 1 | 2 | 3 |
+-------+-----+-----+-----+
| 101 | 138 | 282 | null|
| 105 | 37 | null| 60 |
| 109 | 59 | 97 | 87 |
The result is fine, but now there is one more WEEK added.
I want to develop a CTE with pivot query that will automatically generate distinct weeks and create a column on that basis.
I did some research and found a recursive CTE can be used to do this. I am new to recursive CTE, so please anyone can help me to solve this issue.
I also tried dynamic pivot query but CTE does not allow dynamic query.
Please help.
dynamic pivot doesn't work inside CTE
No, but a CTE works inside a dynamic query:
{assuming you have declared the variables used below}
SELECT #Cols = {query to get the column names in a comma-separated string}
SET #sql='
with CTE as
(
select
*
from
(select
store, week, xCount
from
table_1) src
pivot
(sum(xcount)
for week in ('+#Cols+')
) piv;
)
Select *
From CTE
'
EXEC (#sql)
Can i use recursive CTE?
No this isn't an appropriate use-case for a recursive CTE.
/* Variable to hold unique Week to be used in PIVOT clause */
DECLARE #Weeks NVARCHAR(MAX) = N''
/* Extract unique Week names with pivot formattings */
SELECT #Weeks = #Weeks + ', [' + COALESCE(week, '') + ']'
FROM (SELECT DISTINCT week FROM table_1) DT
/* Remove first comma and space */
SELECT #Weeks = LTRIM(STUFF(#Weeks , 1, 1, ''))
/* Variable to hold t-sql query */
DECLARE #CTEStatement NVARCHAR(MAX) = N''
/* Generate dynamic PIVOT query here */
SET #CTEStatement=N'
;WITH CTE as
( SELECT *
FROM
(SELECT
store
,week
,xCount
FROM
table_1) SRC
PIVOT
(SUM(xcount)
FOR week in ('+ #Weeks +')
) PIV;
)
SELECT *
FROM CTE
'
EXEC (#CTEStatement)

Listing number sequence for financial periods

In SQL 2016, I need to create a list using financial periods but only have the from/to available - it's formatted similar to dates but are 0mmyyyy, so the first 3 numbers are the month/period and the last 4 digits the year.
e.g. period_from is '0102017' and period_to '0032018', but trying to bring back a list that includes the ones in between as well?
0102017,
0112017,
0122017,
0012018,
0022018
Also, the first three characters can go to 012 or 013, so need to be able to easily alter the code for other databases.
I am not entirely sure what you are wanting to use this list for, but you can get all your period values with the help of a tally table and some common table expressions.
-- Test data
declare #p table(PeriodFrom nvarchar(10),PeriodTo nvarchar(10));
insert into #p values('0102017','0032018'),('0052018','0112018');
-- Specify the additional periods you want to include, use 31st December for correct sorting
declare #e table(ExtraPeriodDate date
,ExtraPeriodText nvarchar(10)
);
insert into #e values('20171231','0132017');
-- Convert start and end of periods to dates
with m as (select cast(min(right(PeriodFrom,4) + substring(PeriodFrom,2,2)) + '01' as date) as MinPeriod
,cast(max(right(PeriodTo,4) + substring(PeriodTo,2,2)) + '01' as date) as MaxPeriod
from #p
) -- Built a tally table of dates to join from
,t(t) as (select 1 union all select 1 union all select 1 union all select 1 union all select 1 union all select 1 union all select 1 union all select 1 union all select 1 union all select 1)
,d(d) as (select top (select datediff(month,MinPeriod,MaxPeriod)+1 from m) dateadd(m,row_number() over (order by (select null))-1,m.MinPeriod) from m, t t1, t t2, t t3, t t4, t t5)
-- Use the tally table to convert back to your date period text format
,p as (select d.d as PeriodDate
,'0' + right('00' + cast(month(d) as nvarchar(2)),2) + cast(year(d) as nvarchar(4)) as PeriodText
from d
union all -- and add in any of the addition '13th' month periods you specified previously
select ExtraPeriodDate
,ExtraPeriodText
from #e
)
select PeriodText
from p
order by PeriodDate;
Output:
+------------+
| PeriodText |
+------------+
| 0102017 |
| 0112017 |
| 0122017 |
| 0132017 |
| 0012018 |
| 0022018 |
| 0032018 |
| 0042018 |
| 0052018 |
| 0062018 |
| 0072018 |
| 0082018 |
| 0092018 |
| 0102018 |
| 0112018 |
+------------+
If this isn't what you require exactly it should put you on the right path to generating these values either as the result of a function or concatenated together into a list as per your comment by using for xml on the result by changing the final select statement to:
select stuff((select ', ' + PeriodText
from p
order by PeriodDate
for xml path('')
)
,1,2,'') as PeriodTexts;
Which outputs:
+---------------------------------------------------------------------------------------------------------------------------------------+
| PeriodTexts |
+---------------------------------------------------------------------------------------------------------------------------------------+
| 0102017, 0112017, 0122017, 0132017, 0012018, 0022018, 0032018, 0042018, 0052018, 0062018, 0072018, 0082018, 0092018, 0102018, 0112018 |
+---------------------------------------------------------------------------------------------------------------------------------------+
This is going to be a little complicated. To start, I have a user defined table value function that outputs a calendar table based on a start and end date. You'll want to create that first...
CREATE FUNCTION dbo.udf_calendar (#datestart smalldatetime, #dateend smalldatetime)
RETURNS #calendar TABLE (
[day] int,
[date] smalldatetime
)
AS
BEGIN
DECLARE #rows int
DECLARE #i int = 1
SELECT
#rows = DATEDIFF(DAY, #datestart, #dateend)
WHILE (#i <= #rows)
BEGIN
INSERT INTO #calendar ([day])
VALUES (#i)
SET #i = #i + 1
END
UPDATE a
SET [date] = DATEADD(DAY, [day] - 1, #datestart)
--select *, DATEADD(day,id-1,#datestart)
FROM #calendar a
RETURN
END
Then, the following will give you the output that I THINK you are looking for. I've commented to try and explain how I got there, but it still might be a bit difficult to follow...
--Create temp table example with your period from and to.
IF (SELECT
OBJECT_ID('tempdb..#example'))
IS NOT NULL
DROP TABLE #example
SELECT
'0102017' periodfrom,
'0032018' periodto INTO #example
/*
This is the difficult part. Basically you're inner joining the calendar
to the temp table where the dates are between the manipulated period from and to.
I've added an extra column formatted to allow ordering correctly by period.
*/
SELECT DISTINCT
periodfrom,
periodto,
RIGHT('00' + CAST(DATEPART(MONTH, [date]) AS varchar(50)), 3) + CAST(DATEPART(YEAR, [date]) AS varchar(50)) datefill,
CAST(DATEPART(YEAR, [date]) AS varchar(50)) + RIGHT('00' + CAST(DATEPART(MONTH, [date]) AS varchar(50)), 3) datefill2
FROM dbo.udf_calendar('2015-01-01', '2018-12-31') a
INNER JOIN #example b
ON a.[date] BETWEEN SUBSTRING(periodfrom, 2, 2) + '-01-' + SUBSTRING(periodfrom, 4, 4) AND SUBSTRING(periodto, 2, 2) + '-01-' + SUBSTRING(periodto, 4, 4)
ORDER BY datefill2

Insert random Data content in SQL Server 2008

I know there are several topics on this, but none of them was suitable for me, that's why I took the chance to ask you again.
I have a table which has columns UserID, FirstName, Lastname.
I need to insert 300 000 records for each column and they have to be unique, for example:
UserID0001, John00001, Doe00001
UserID0002, John00002, Doe00002
UserID0003, John00003, Doe00003
I hope there is an easy way :)
Thank you in advance.
Best,
Lyubo
;with sequence as (
select N = row_number() over (order by ##spid)
from sys.all_columns c1, sys.all_columns c2
)
insert into [Table] (UserID, FirstName, Lastname)
select
'UserID' + right('000000' + cast(N as varchar(10)), 6),
'John' + right('000000' + cast(N as varchar(10)), 6),
'Doe' + right('000000' + cast(N as varchar(10)), 6)
from sequence where N <= 300000
You could use the ROW_NUMBER function to generate different numbers like this:
SQL Fiddle
MS SQL Server 2008 Schema Setup:
CREATE TABLE dbo.users(
Id INT IDENTITY(1,1) PRIMARY KEY CLUSTERED,
user_id VARCHAR(20),
first_name VARCHAR(20),
last_name VARCHAR(20)
);
GO
DECLARE #NoOfRows INT = 7;
INSERT INTO dbo.users(user_id, first_name, last_name)
SELECT 'User_'+n, 'John_'+n, 'Doe_'+n
FROM(
SELECT REPLACE(STR(ROW_NUMBER()OVER(ORDER BY (SELECT NULL))),' ','0') n FROM(
select TOP(#NoOfRows) 1 x from sys.objects A,sys.objects B,sys.objects C,sys.objects D,sys.objects E,sys.objects F,sys.objects G
)X
)N
Query 1:
SELECT * FROM dbo.users
Results:
| ID | USER_ID | FIRST_NAME | LAST_NAME |
-----------------------------------------------------------
| 1 | User_0000000001 | John_0000000001 | Doe_0000000001 |
| 2 | User_0000000002 | John_0000000002 | Doe_0000000002 |
| 3 | User_0000000003 | John_0000000003 | Doe_0000000003 |
| 4 | User_0000000004 | John_0000000004 | Doe_0000000004 |
| 5 | User_0000000005 | John_0000000005 | Doe_0000000005 |
| 6 | User_0000000006 | John_0000000006 | Doe_0000000006 |
| 7 | User_0000000007 | John_0000000007 | Doe_0000000007 |
Just change the #NoOfRows to 300000 to get the number of rows you are looking for.
I've adapted a script found in this article:
DECLARE #RowCount INT
DECLARE #RowString VARCHAR(14)
DECLARE #First VARCHAR(14)
DECLARE #LAST VARCHAR(14)
DECLARE #ID VARCHAR(14)
SET #ID = 'UserID'
SET #First = 'John'
SET #Last = 'Doe'
SET #RowCount = 1
WHILE #RowCount < 300001
BEGIN
SET #RowString = CAST(#RowCount AS VARCHAR(10))
SET #RowString = REPLICATE('0', 6 - DATALENGTH(#RowString)) + #RowString
INSERT INTO TestTableSize (
UserID
,FirstName
,LastName
)
VALUES
(#ID + #RowString
, #First + #RowString
, #Last + #RowString)
SET #RowCount = #RowCount + 1
END

SQL Server Pivots: Displaying row values to column headers

I have a table (items) which is in the following format:
ITEMNO | WEEKNO | VALUE
A1234 | 1 | 805
A2345 | 2 | 14.50
A3547 | 2 | 1396.70
A2208 | 1 | 17.65
A4326 | 6 | 19.99
It's a table which shows the value of sales for items in a given week.
The results or what I want to display in a table format is the item number in a row followed by columns for each week containing the values, e.g.
ITEMNO | WK1 | WK2 | WK3 | WK4 | WK5 ...etc up to 52
A1234 | 805 | 345 | 234 | 12 | 10 ...etc up to 52
A2345 | 23 | 12 | 456 | 34 | 99 ...etc up to 52
A3456 | 234 | 123 | 34 | 25 | 190 ...etc up to 52
Although I've 52...so I've only data for up to week9 but that will increase with time.
So basically what it is I'm looking to display is the week number value as a column header.
Is this possible...although I'm tempted to just grab the data and display properly through code/(asp.net) but I was wondering if there was away to display it like this in SQL?
Does anyone know or think that that this might be the best way?
There are two ways of doing this with static SQL and dynamic SQL:
Static Pivot:
SELECT P.ItemNo, IsNull(P.[1], 0) as Wk1, IsNull(P.[2], 0) as Wk2
, IsNull(P.[3], 0) as Wk3, IsNull(P.[4], 0) as Wk4
, IsNull(P.[5], 0) as Wk5, IsNull(P.[6], 0) as Wk6
, IsNull(P.[7], 0) as Wk7, IsNull(P.[8], 0) as Wk8
, IsNull(P.[9], 0) as Wk9
FROM
(
SELECT ItemNo, WeekNo, [Value]
FROM dbo.Items
) I
PIVOT
(
SUM([Value])
FOR WeekNo IN ([1], [2], [3], [4], [5], [6], [7], [8], [9])
) as P
Dynamic Pivot:
DECLARE
#cols AS NVARCHAR(MAX),
#y AS INT,
#sql AS NVARCHAR(MAX);
-- Construct the column list for the IN clause
SET #cols = STUFF(
(SELECT N',' + QUOTENAME(w) AS [text()]
FROM (SELECT DISTINCT WeekNo AS W FROM dbo.Items) AS W
ORDER BY W
FOR XML PATH('')),
1, 1, N'');
-- Construct the full T-SQL statement
-- and execute dynamically
SET #sql = N'SELECT *
FROM (SELECT ItemNo, WeekNo, Value
FROM dbo.Items) AS I
PIVOT(SUM(Value) FOR WeekNo IN(' + #cols + N')) AS P;';
EXEC sp_executesql #sql;
GO
Maybe something like this:
Test data
CREATE TABLE #tbl
(
ITEMNO VARCHAR(100),
WEEKNO INT,
VALUE FLOAT
)
INSERT INTO #tbl
VALUES
('A1234',1,805),
('A2345',2,14.50),
('A3547',2,1396.70),
('A2208',1,17.65),
('A4326',6,19.99)
Week columns
DECLARE #cols VARCHAR(MAX)
;WITH Nbrs ( n ) AS (
SELECT 1 UNION ALL
SELECT 1 + n FROM Nbrs WHERE n < 52 )
SELECT #cols = COALESCE(#cols + ','+QUOTENAME('WK'+CAST(n AS VARCHAR(2))),
QUOTENAME('WK'+CAST(n AS VARCHAR(2))))
FROM
Nbrs
Just the included weeks
DECLARE #cols VARCHAR(MAX)
;WITH CTE
AS
(
SELECT
ROW_NUMBER() OVER(PARTITION BY WEEKNO ORDER BY WEEKNO) AS RowNbr,
WEEKNO
FROM
#tbl
)
SELECT #cols = COALESCE(#cols + ','+QUOTENAME('WK'+CAST(WEEKNO AS VARCHAR(2))),
QUOTENAME('WK'+CAST(WEEKNO AS VARCHAR(2))))
FROM
CTE
WHERE
CTE.RowNbr=1
Dynamic pivot
DECLARE #query NVARCHAR(4000)=
N'SELECT
*
FROM
(
SELECT
tbl.ITEMNO,
''WK''+CAST(tbl.WEEKNO AS VARCHAR(2)) AS WEEKNO,
tbl.VALUE
FROM
#tbl as tbl
) AS p
PIVOT
(
SUM(VALUE)
FOR WEEKNO IN ('+#cols+')
) AS pvt'
EXECUTE(#query)
Drop the temp table
DROP TABLE #tbl
Use Pivot, although quite a bit of code..
If you create report in reporting services, can use matrix..
Follow the below walkthrogh which explains it clearly
http://www.tsqltutorials.com/pivot.php
You can use PIVOT if you want to do this in sql directly.
It can be more efficient to use the SQL Server to do this as opposed to the client depending upon the size of the data and the aggregation.

Resources