Pivoting for DATEPART(wk, date) - sql

I have a table like this,
mytable
date | value
2018.09.12 | 1
2018.09.11 | 2
2018.09.10 | 3
I need a query to return sum(value) for the last six weeks. Exactly like this.
week# | value
37 | 6
36 | 0
35 | 0
34 | 8
33 | 9
32 | 10
31 | 11
I have a query to return sumvalue for each week.
SELECT Sum(Value) AS Sumvalue, DATEPART(wk, date) AS [weekNo]
FROM mytable
WHERE Date BETWEEN DATEADD(DAY, -42, GETDATE()) AND GETDATE()
GROUP BY DATEPART(wk, date)
But this can't handle zero values for a week.
How can write a pivoted query to obtain the format?
My try;
SELECT *
FROM (
SELECT Value, DATEPART(wk, date) AS [weekNo]
FROM mytable
WHERE Date BETWEEN DATEADD(DAY, -42, GETDATE()) AND GETDATE()
) As sourcetable
PIVOT
(
Sum(Value) for DATEPART(wk, date) IN (SELECT date FROM mytable where date between
DATEADD(DAY, -42, GETDATE()) and GETDATE())
) AS pivotable
I am getting the syntax error near for keyword. How can I put the six weeks in pivot statemet

Found a partial solution!
SELECT *
FROM (
SELECT Value, DATEPART(wk, date) AS [weekNo]
FROM mytable
WHERE Date BETWEEN DATEADD(DAY, -42, GETDATE()) AND GETDATE()
) As sourcetable
PIVOT
(
Sum(Value) for [week] IN ([1], [2], [3], ... ,[54])
) AS pivotable
It is partial since it is kind of hardcoding the for statement and cannot control the unnecessary weeks in the query.

Related

Keep last n business days records from today date in SQL Server

How can we keep last n business days records from today date in this table:
Suppose n = 7
Sample Data:
Table1:
Date
----------
2021-11-29
2021-11-30
2021-12-01
2021-12-02
2021-12-03
2021-12-04
2021-12-05
2021-12-06
2021-12-07
2021-12-08
2021-12-09
2021-12-10
2021-12-11
2021-12-12
2021-12-13
Based on this table data we want output like below. It should delete all the rows before the 03-Dec or data for last 7 business days.
Date
-------
2021-12-03
2021-12-06
2021-12-07
2021-12-08
2021-12-09
2021-12-10
2021-12-13
Note: It's fine if we keep data for Saturday, Sunday in between business days.
I tried this query
DECLARE #n INT = 7
SELECT * FROM Table1
WHERE [date] < Dateadd(day, -((#n + (#n / 5) * 2)), Getdate())
but Saturday, Sunday logic doesn't fit here with my logic. Please suggest better approach.
You can get the 7th working day from today as
select top(1) cast(dateadd(d, -n + 1, getdate()) as date) d
from (
select n
, sum (case when datename(dw, dateadd(d, -n + 1, getdate())) not in ('Sunday', 'Saturday') then 1 end) over(order by n) wdn
from (
values (1),(2),(3),(4),(5),(6),(7),(8),(9),(10),(11)
)t0(n)
) t
where wdn = 7
order by n;
Generally using on-the-fly tally for a #n -th day
declare #n int = 24;
with t0(n) as (
select n
from (
values (1),(2),(3),(4),(5),(6),(7),(8),(9),(10)
) t(n)
), tally as (
select top(#n + (#n/5 +1)*2) row_number() over(order by t1.n) n
from t0 t1, t0 t2, t0 t3
)
select top(1) cast(dateadd(d, -n + 1, getdate()) as date) d
from (
select n
, sum (case when datename(dw, dateadd(d, -n + 1, getdate())) not in ('Sunday', 'Saturday') then 1 end) over(order by n) wdn
from tally
) t
where wdn = #n
order by n;
You can use CTE to mark target dates and then delete all the others from the table as follows:
; With CTE As (
Select [Date], Row_number() Over (Order by [Date] Desc) As Num
From tbl
Where DATEPART(weekday, [Date]) Not In (6,7)
)
Delete From tbl
Where [Date] Not In (Select [Date] From CTE Where Num<=7)
If the number of business days in the table may be less than 7 and you need to bring the total number of days to 7 by adding days off, try this:
Declare #n Int = 7
; With CTE As (
Select [Date], IIF(DATEPART(weekday, [Date]) In (6,7), 0, 1) As IsBusinessDay
From tbl
)
Delete From tbl
Where [Date] Not In (Select Top(#n) [Date] From CTE Order By IsBusinessDay Desc, [Date] Desc)
If there is only one date for each day, you can simply do this:
SELECT TOP 7 [Date] FROM Table1
WHERE
[Date] < GETDATE() AND DATENAME(weekday, [DATE]) NOT IN ('Saturday', 'Sunday')
ORDER BY
[DATE] DESC

Multiple counts and merge columns

I current have a query that grabs the number of parts made per hour between two dates:
DECLARE #StartDate datetime
DECLARE #EndDate datetime
SET #StartDate = '10/10/2018'
SET #EndDate = '11/11/2018'
SELECT
CONVERT(VARCHAR(10), CAST(presstimes AS DATE), 111) AS ForDate,
DATEPART(HOUR, presstimes) AS OnHour,
COUNT(*) AS Totals
FROM
partmasterlist
WHERE
((presstimes >= #StartDate AND presstimes < dateAdd(d, 1, #EndDate))
AND (((presstimes IS NOT NULL))))
GROUP BY
CONVERT(VARCHAR(10), CAST(presstimes AS DATE), 111),
DATEPART(HOUR, presstimes)
ORDER BY
CONVERT(VARCHAR(10), CAST(presstimes AS DATE), 111) ASC;
Output:
Date Hour QTY
---------------------
2018/11/06 11 16
2018/11/06 12 20
2018/11/06 13 29
2018/11/06 14 26
Now I need to add another qty column to count where "trimmingtimes" is set.
I can't figure out how to full join the date and hour columns (e.g. presstimes might have 20qty for Hour 2, but trimmingtimes is NULL for Hour 2);
Input:
ID presstimes trimmingtimes
-----------------------------------------------------------------
1 2018-10-10 01:15:23.000 2018-10-10 01:15:23.000
2 2018-10-10 01:15:23.000 NULL
3 2018-10-10 02:15:23.000 NULL
4 NULL 2018-10-10 03:15:23.000
Output:
Date hour Press QTY T QTY
------------------------------------
10/10/18 1 2 1
10/10/18 2 1 0
10/10/18 3 0 1
I suspect you want something like this:
select convert(date, v.dt) as date,
datepart(hour, v.dt) as hour,
sum(ispress) as num_press,
sum(istrim) as num_trim
from partmasterlist pml cross apply
(values (pml.presstime, 1, 0), (pml.trimmingtime, 0, 1)
) v(dt, ispress, istrim)
group by convert(date, v.dt), datepart(hour, v.dt)
order by convert(date, v.dt), datepart(hour, v.dt);
You can add a where clause for a particular range.

How to get previous 7 days' data from today in SQL Server

I have a DataEntry Table called GuestAddressData(UserId INT, EDate DateTime) with users data. I need to fetch the count of users for today to previous 7 Days. My Query:
SELECT
row_number() over (order by (SELECT 1)) ID,
count(*) Total,
LEFT(Datename(weekday, Cast(EDate as date)), 3) Day
FROM
CRM0001GuestAddressData
WHERE
EDate >= dateadd(week, datediff(d, -1, getdate()-2)/7, -1)
GROUP BY
Cast(EDate as date)
ORDER BY
Cast(EDate as date)
For example if today is Friday then my expected output is:
ID | TOTAL | DAY
------------------------
1 | 78 | Sat
2 | 23 | Sun
3 | 54 | Mon
4 | 17 | Tues
5 | 56 | Wed
6 | 45 | Thus
7 | 78 | Fri - Today
but this is not correct. How to solve it?
You can "generate" a list of seven numbers and use it to build the desired dates. Then left join with your data to get the counts, including zeros:
WITH datelist(num, a, b) AS (
SELECT num, DATEADD(DAY, -num, CAST(CURRENT_TIMESTAMP AS DATE)), DATEADD(DAY, -num + 1, CAST(CURRENT_TIMESTAMP AS DATE))
FROM (VALUES (0), (1), (2), (3), (4), (5), (6)) AS v(num)
)
SELECT 7 - num AS ID, datelist.a AS Day, COUNT(IDBooking)
FROM datelist
LEFT JOIN T_Bookings ON Opened >= datelist.a AND Opened < datelist.b
GROUP BY datelist.a, datelist.num
ORDER BY datelist.a
SELECT
row_number() over (order by dDate) ID,
cnt,
LEFT(Datename(weekday, dDate), 3) Day
from
(Select cast(EDate as Date) as dDate,
count(*) as cnt
FROM (values (0),(1),(2),(3),(4),(5),(6)) t(v)
inner join
CRM0001GuestAddressData gd on datediff(d, gd.Edate, getdate()) = t.v
WHERE
EDate >= dateadd(d, -6, cast(getdate() as date)) and EDate < dateadd(d,1,cast(getdate() as date))
GROUP BY
Cast(EDate as date)) tmp;
Note: You meant to get 7 days from yesterday, right? Nevermind, corrected based on your sample.
DBFiddle demo
EDIT: Having all days:
SELECT
row_number() over (order by dDate) ID,
cnt,
LEFT(Datename(weekday, dDate), 3) Day
from
(Select dateadd(d,-v,cast(getdate() as date)) as dDate,
count(Edate) as cnt
FROM (values (0),(1),(2),(3),(4),(5),(6)) t(v)
left join
CRM0001GuestAddressData gd on Datediff(d,gd.EDate, getdate()) = t.v
GROUP BY
dateadd(d,-v,cast(getdate() as date))) tmp;
DBFiddle Demo

Adding Missing Dates From SQL Query

I am trying to add missing dates to my query so that my results look like this:
10/22/2018 15
10/21/2018 0
10/20/2018 14
Rather than this:
10/22/2018 15
10/20/2018 14
I want the past 300 days listed, even if the output value is 0.
Here is my query:
SELECT TOP (300)
CAST(createddate as DATE),
count(DISTINCT ID)
FROM table
GROUP BY CAST(createddate as DATE)
ORDER BY CAST(createddate as DATE) DESC
You can use a recursive CTE to generate the data:
WITH dates as (
SELECT MAX(CAST(createddate as date)) as dte, 1 as lev
FROM table
UNION ALL
SELECT DATEADD(day, -1, dte), lev + 1
FROM dates
WHERE lev < 300
)
SELECT COUNT(DISTINCT t.ID)
FROM dates d LEFT JOIN
table t
ON d.dte = CAST(t.createddate as DATE)
GROUP BY d.dte
ORDER BY d.dte DESC
OPTION (MAXRECURSION 0);

MSSQL Subtract two rows from each other in union

So I have a successful query that shows the first row of last month, and the first row of this month. This query shows fields that have ongoing counters for electricity usage.
I need the difference of a particular field, and then multiply that by .54802 and round to nearest hundredth.
SELECT *
FROM
(
SELECT TOP (1) *
FROM Datebase
WHERE DATEPART(M, Timestamp) = DATEPART(M, DATEADD(M, -1, GETDATE()))
AND DATEPART(Yyyy, Timestamp) = DATEPART(Yyyy, DATEADD(M, 0, GETDATE()))
ORDER BY Timestamp ASC
) AS A
UNION
SELECT *
FROM
(
SELECT TOP (1) *
FROM Datebase
WHERE DATEPART (M, Timestamp) = DATEPART(M, DATEADD(M, 0, GETDATE()))
AND DATEPART(Yyyy, Timestamp) = DATEPART(YYYY, DATEADD(M, 0, GETDATE()))
ORDER BY Timestamp ASC
) AS B;
This outputs:
| ID | Timestamp | 7000AV119 |
---------------------------------------------------
| 1 | 2018-08-01 00:00:03.000 | 3675.59 |
| 2 | 2018-09-01 00:00:03.000 | 3750.93 |
I need it to show:
| ID | Timestamp | 7000AV119 | Difference
-----------------------------------------------------------
| 1 | 2018-08-01 00:00:03.000 | 3675.59 | 0
| 2 | 2018-09-01 00:00:03.000 | 3750.93 | 41.29
I would get the two records as:
select t.*
from (select t.*,
row_number() over (partition by year(timestamp), month(timestamp) order by timestamp asc) as seqnum
from Datebase t
where timestamp >= dateadd(day, 1, eomonth(getdate(), -2))
) t
where seqnum = 1;
Then I would use lag():
select t.*,
round(([7000AV119] - lag([7000AV119]) over (order by timestamp)
) * 0.54802,
2
)
from (select t.*,
row_number() over (partition by year(timestamp), month(timestamp) order by timestamp asc) as seqnum
from Datebase t
where timestamp >= dateadd(day, 1, eomonth(getdate(), -2))
) t
where seqnum = 1;
This returns NULL rather than 0 for the first row. That makes more sense to me.
Try with lag function
with testcte as
(
select *
from (select top(1) * from DATABASE
WHERE DATEPART(m, Timestamp) = DATEPART(m, DATEADD(m, -1, getdate()))
AND DATEPART(yyyy, Timestamp) = DATEPART(yyyy, DATEADD(m, 0, getdate()))
order by Timestamp ASC) a
union
select *
from (select top(1) * from DATABASE
WHERE DATEPART(m, Timestamp) = DATEPART(m, DATEADD(m, 0, getdate()))
AND DATEPART(yyyy, Timestamp) = DATEPART(yyyy, DATEADD(m, 0, getdate()))
order by Timestamp ASC) b
)
select id,Timestamp,7000AV119,7000AV119- LAG(7000AV119,1,0) OVER (ORDER BY id) AS Diff
from testcte