MSSQL Subtract two rows from each other in union - sql

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

Related

Unpivot subquery

i need to transform this query result into rows instead of columns. Using Unpivot is possible, but I was not able to do this activity
see the code:
SELECT
cast(IIF(DATEPART(HOUR, [DATA]) <= 4, DATEADD(DAY, -1, [DATA]), [DATA]) as date) as Data,
IIF(DATEPART(HOUR, [DATA]) <= 4, DATEADD(DAY, -1, [DATA]), [DATA]) AS DataHora,
SUM([FraturaContusao]) AS 'FraturaContusao',
SUM([Salpingite]) AS 'Salpingite'
FROM
(
SELECT
[DATA], ROMANEIO,
IIF(( bAbaco.FRATURA_CONTUSAO - COALESCE(LAG(bAbaco.FRATURA_CONTUSAO) OVER (ORDER BY bAbaco.ID), 0)) > 0 , ( bAbaco.FRATURA_CONTUSAO - COALESCE(LAG(bAbaco.FRATURA_CONTUSAO) OVER ( ORDER BY bAbaco.ID), 0)), 0) AS 'FraturaContusao',
IIF(( bAbaco.Salpingite - COALESCE(LAG(bAbaco.Salpingite) OVER (ORDER BY bAbaco.ID), 0)) > 0 , ( bAbaco.Salpingite - COALESCE(LAG(bAbaco.Salpingite) OVER ( ORDER BY bAbaco.ID), 0)), 0) AS 'Salpingite'
FROM
ContProdUIA.ABACO_DIGITAL_L2 bAbaco
) as x
WHERE
CONVERT(varchar, x.[DATA], 23) >= DATEADD(DAY, -16, GETDATE())
GROUP BY
IIF(DATEPART(HOUR, [DATA]) <= 4, DATEADD(DAY, -1, [DATA]), [DATA])
this is the result of the query:
I need the columns 'FraturaContusao' and 'Salpingite' to be transformed with unpviot
something like:
Data| DataHora | Tipo| Qty
2020-06-23 | 2020-06-23 15:54 | FraturaContusao| 15
2020-06-23 | 2020-06-23 15:54 | Salpingite | 20
Perhaps the easiest is to use a CROSS APPLY
Example
Select A.Data
,A.DataHora
,B.Tipo
,B.Qty
From (
-- your orignal query here --
) A
Cross Apply ( values ('FraturaContusao',FraturaContusao)
,('Salpingite' ,Salpingite)
) B(Tipo,Qty)

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

Pivoting for DATEPART(wk, date)

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.

months between two dates in sql server with starting and end date of each of them in sql server

i want to get months between two dates with their starting and end dates.Suppose if i enter startdate as "2017-04-01" and enddate as "2017-07-31", i want list of months i.e April,May,June,July with their starting and end date respectively.Kindly suggest me how it can be achieved.
One method is a recursive CTE:
with cte as (
select dateadd(day, 1 - day(#startdate), #startdate) as som,
eomonth(#startdate) as eom
union all
select dateadd(month, 1, som), eomonth(dateadd(month, 1, som))
from cte
where dateadd(month, 1, som) < #enddate
)
select *
from cte;
If you want the name of the month, then you can use datename(month, som).
Without recursion, using master.dbo.spt_values as a substitute for a numbers table:
declare #StartDate date = '20170401'
, #EndDate date = '20170731';
;with Months as (
select top (datediff(month,#startdate,#enddate)+1)
[Month] = dateadd(month, row_number() over (order by number) -1, #StartDate)
, MonthEnd = dateadd(day,-1,dateadd(month, row_number() over (order by number), #StartDate))
from master.dbo.spt_values
order by [Month]
)
select * from Months;
rextester demo: http://rextester.com/FXQJ4048
returns:
+------------+------------+
| Month | MonthEnd |
+------------+------------+
| 2017-04-01 | 2017-04-30 |
| 2017-05-01 | 2017-05-31 |
| 2017-06-01 | 2017-06-30 |
| 2017-07-01 | 2017-07-31 |
+------------+------------+
When generating a set or sequence in SQL Server, methods that avoid recursion and loops perform significantly better as the number of values increases.
Reference:
Generate a set or sequence without loops - 1 - Aaron Bertrand
Generate a set or sequence without loops - 2 - Aaron Bertrand
Generate a set or sequence without loops - 3 - Aaron Bertrand
To get the start and end dates of each month within a given range, when the value of the #StartDate parameter is not the first day of the month:
The first option is to truncate the #StartDate parameter to the first of the month, the second option is to adjust the expressions in the common table expression to truncate the values there:
declare #StartDate date = '20170415'
, #EndDate date = '20170715';
/* Option 1: truncate #StartDate to the beginning of the month */
--set #StartDate = dateadd(month, datediff(month, 0, #StartDate), 0);
/* Option 2: Truncate #StartDate to month in the common table expression: */
;with Months as (
select top (datediff(month,#StartDate,#EndDate)+1)
[Month] = dateadd(month
, datediff(month, 0, #StartDate) + row_number() over (order by number) -1
, 0)
, MonthEnd = dateadd(day,-1,dateadd(month
, datediff(month, 0, #StartDate) + row_number() over (order by number)
,0))
from master.dbo.spt_values
order by [Month]
)
select * from Months;
Here you go...
created the schema
create table abc(
date1 date
)
//Inserting data into it
insert into abc values(getdate()),
(DATEADD(Month, -1, getdate())),
(DATEADD(Month, -2, getdate())),
(DATEADD(Month, -3, getdate())),
(DATEADD(Month, -4, getdate()))
and finally the Select Query to fetch the data between Start date and end date:
select (datename(Month, date1)+' '+convert(varchar(2), date1, 103)) as [Date] from abc
where convert(varchar(10), date1, 120) between '2017-05-02' and '2017-07-02'
Another approach to fetch the between two dates data:
select (datename(Month, date1)+' '+convert(varchar(2), date1, 103)) as [Date] from abc
where date1 >= (DATEADD(Month, -3, getdate())) AND date1 <=getdate();
And the returned result is:
this is the Fiddle where you can test this query -> SQL FIDDLE
Simple and easy...good luck bro :)
Try this:
DECLARE #Start DATE ='2017-04-01',
#End DATE ='2017-07-31'
SELECT *, Datename(mm, date),
Dateadd(mm, Datediff(mm, 0, date), 0) AS FirstDateOfMonth,
Dateadd (dd, -1, Dateadd(mm, Datediff(mm, 0, date) + 1, 0)) as
LastDateOfMonth
FROM dbo.TableName
WHERE Cast(date AS DATE) BETWEEN #Start AND #End
If this is not just a one-off report, then I would create a calendar table, and use that to "group by". This will also let you do many other date related calculations.
You can find one at simple calendar or one here at Stackoverflow
Then your code could look like this:
SELECT top 10000 * FROM dbo.calendar DD
WHERE DD.TimeStampFrom>='2017-04-01' AND DD.TimeStampFrom <='2017-07-31'
AND DAY(DD.TimeStampFrom)=1
I created a stored procedure for that, may be you can convert that into user defined Function.
Posting that code below,
create procedure ListMonths
#date1 date,#date2 date
as
begin
create Table #tempTable
(mnth varchar(10))
while #date1<#date2
begin
insert into #tempTable
select DATENAME(month,#date1)
set #date1 = DATEADD(MONTH,1,#date1)
end
select * from #tempTable;
drop table #tempTable;
end
To execute the stored procedure:
exec ListMonths '2017-04-01','2018-01-31'
output
+------------+
| mnth |
+------------+
| April |
| May |
| June |
| July |
| August |
| September |
| October |
| November |
| December |
| January |
+------------+
result

Draw a dynamic table in SQL

Good morning, I'm trying to draw a dynamic table with some data, this query, draw a table that has a day, his week, and some data that I want to calculate dynamically.
This is my query
use Alfri
;with monthDates
as
(
select DATEADD(month, 0, CONVERT(DATE,'2013-09-09',102)) as d
,DATEPART(week, DATEADD(month, datediff(month, 0, '2013-09-09'),0)) as w,
(
SELECT SUM(
CASE WHEN arrive_yard IS NOT NULL THEN
DATEDIFF(mi, Solicitado, Libre)-DATEDIFF(mi, arrive_yard, leave_yard)
ELSE
DATEDIFF(mi, Solicitado, Libre)
END
) as Tiempo
FROM MovimientoHoras
WHERE CONVERT(DATE, Solicitado, 102) = '2013-10-11'
) as info
union all
select DATEADD(day, 1, d)
,DATEPART(week, DATEADD(day, 1, d))
, info
from monthDates
where d < DATEADD(month, datediff(month, 0, '2013-10-09')+1,-1)
)
SELECT * FROM monthDates
This query draw me a table like this.
d |w |info |
2013-09-09 | 36 | 2780|
2013-09-10 | 37 | 2780|
2013-09-11 | 37 | 2780|
2013-09-12 | 37 | 2780|
2013-09-13 | 37 | 2780|
2013-09-14 | 37 | 2780|
2013-09-15 | 37 | 2780|
2013-09-16 | 37 | 2780|
But the info's column isn't calculling dynamically and this is my dilenma.
The point is that column d is calculated dynamically and that's the value that I want to use in info's column query, something like this WHERE CONVERT(DATE, Solicitado, 102) = d) as info instead of WHERE CONVERT(DATE, Solicitado, 102) = '2013-10-11') as info where D is the date changing in every row, the way that I'm trying it just giving me same data of '2013-10-11'
Something like a While to change a day in that subquery
Thanks
The basic approach is to separate the part that generates dates from the part that calculates the info for that date:
;with monthDates as (
select
cast('20130909' as date) as d,
datepart(week, dateadd(month, datediff(month, 0, '2013-09-09'), 0)) as w
union all
select
dateadd(day, 1, d),
datepart(week, dateadd(day, 1, d))
from
monthDates
where
d < dateadd(month, datediff(month, 0, '2013-10-09') + 1, -1)
)
select
m.d,
m.w,
sum(
datediff(mi, Solicitado, Libre)
- case when arrive_yard is not null then
datediff(mi, arrive_yard, leave_yard)
else 0 end
) info
from
monthDates m
left outer join
MovimientoHoras h
on cast(Solicitado as date) = m.d
group by
m.d,
m.w
Example SQLFiddle