Unpivot subquery - sql

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)

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

How do I delete rows greater than 6 months old , but ensure I have at least 100 rows left in a table using SQL?

Consider the following table definition:
FooTable
ID int,
TimeStamp datetime,
Data varchar(50)
I would like to keep only the recent 6 months of data, but also make sure that I keep a minimum of 100 rows of data in the table. How do I do this?
I know how to remove data greater than 6 months old:
DELETE
FROM FooTable
WHERE TimeStamp <= DATEADD(MONTH, -6, GETDATE())
How do I add the condition of maintaining at least 100 rows of data in the table (regardless of how hold the data is)?
Thanks, JohnB
I think you want:
WITH todelete AS (
SELECT f.*,
ROW_NUMBER() OVER (ORDER BY TimeStamp DESC) as seqnum
FROM FooTable
)
DELETE FROM todelete
WHERE seqnum > 100 AND
TimeStamp < DATEADD(MONTH, -6, GETDATE());
This deletes the oldest timestamp but being sure that they are deleted only after the 100th record.
I believe you will use this code in SSMS to clean data so you will not care if it is including some variables.
So, my solution is this:
DECLARE #CURRENT_NO INT
SELECT #CURRENT_NO = COUNT(*) FROM xTable WHERE timstamp < DATEADD(MONTH, -6, GETDATE())
DECLARE #DEL_NO INT
SELECT #DEL_NO = CASE WHEN #CURRENT_NO < 100 THEN 100 ELSE #CURRENT_NO END
DECLARE #DEL_ROW_NO INT
SELECT #DEL_ROW_NO = COUNT(*) + #DEL_NO - 100 FROM xTable WHERE timstamp > DATEADD(MONTH, -6, GETDATE())
DELETE TOP (#DEL_ROW_NO) FROM xTable WHERE timstamp < DATEADD(MONTH, -6, GETDATE())
SELECT * FROM xTable
I have tested it using the script below (minimised version):
CREATE TABLE xTable (timstamp DATETIME, qty INT)
INSERT INTO xTable VALUES
('2020-11-01', 5), ('2020-01-01', 5), ('2020-01-01', 5), ('2020-01-01', 5), ('2020-01-01', 5),
('2020-11-01', 5), ('2020-01-01', 5), ('2020-01-01', 5), ('2020-01-01', 5), ('2020-01-01', 5)
DECLARE #CURRENT_NO INT
SELECT #CURRENT_NO = COUNT(*) FROM xTable WHERE timstamp < DATEADD(MONTH, -6, GETDATE())
DECLARE #DEL_NO INT
SELECT #DEL_NO = CASE WHEN #CURRENT_NO < 5 THEN 5 ELSE #CURRENT_NO END
SELECT #DEL_NO
DECLARE #DEL_ROW_NO INT
SELECT #DEL_ROW_NO = COUNT(*) + #DEL_NO - 5 FROM xTable WHERE timstamp > DATEADD(MONTH, -6, GETDATE())
SELECT #DEL_ROW_NO
DELETE TOP (#DEL_ROW_NO) FROM xTable WHERE timstamp < DATEADD(MONTH, -6, GETDATE())
SELECT * FROM xTable
DROP TABLE xTable
and the result is :
+-------------------------+-----+
| timstamp | qty |
+-------------------------+-----+
| 2020-11-01 00:00:00.000 | 5 |
| 2020-11-01 00:00:00.000 | 5 |
| 2020-01-01 00:00:00.000 | 5 |
| 2020-01-01 00:00:00.000 | 5 |
| 2020-01-01 00:00:00.000 | 5 |
+-------------------------+-----+

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

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

How to ORDER BY in SQL PIVOT

I currently have this query using PIVOT generating a table like this:
USER | DEC | NOV | OCT
---------------------------------
bob | 3 | 5 | 2
jon | 7 | 0 | 1
tim | 4 | 2 | 6
What I would like to do but it looks like a stretch is to ORDER BY the results by the DEC value descending.
This is the query:
with Mth (st, nd) as (
select DATEADD (M, datediff (m, 0,'2012-09-01'), 0),
DATEADD (M, DATEDIFF (m, 0, '2012-09-01') + 1, 0)
union all
select DATEADD (m, 1, st),
DATEADD (m, 1, nd)
from Mth
where nd <= DATEADD (m, datediff (m, 0, getdate()), 0)
)
select *
from
(
select MONTH(Mth.st) Month,
U.USER,
COUNT(S.QRY_ID) Searches
FROM Mth
LEFT JOIN SEARCHES S
on Mth.st <= S.CREATED
and Mth.nd > S.CREATED
LEFT JOIN MEMBERS U
on U.AID = S.AID
GROUP BY YEAR(Mth.st), MONTH(Mth.st), U.HOLDER_LOGIN
) src
pivot
(
sum(searches)
for month in ([12],[11],[10])
) piv
Doing piv ORDER BY piv.Searches gives an error so is it possible to specify the column?
Try this:
with Mth (st, nd) as (
select DATEADD (M, datediff (m, 0,'2012-09-01'), 0),
DATEADD (M, DATEDIFF (m, 0, '2012-09-01') + 1, 0)
union all
select DATEADD (m, 1, st),
DATEADD (m, 1, nd)
from Mth
where nd <= DATEADD (m, datediff (m, 0, getdate()), 0)
), Pivoted
AS
(
select *
from
(
select MONTH(Mth.st) Month,
U.USER,
COUNT(S.QRY_ID) Searches
FROM Mth
LEFT JOIN SEARCHES S
on Mth.st <= S.CREATED
and Mth.nd > S.CREATED
LEFT JOIN MEMBERS U
on U.AID = S.AID
GROUP BY YEAR(Mth.st), MONTH(Mth.st), U.HOLDER_LOGIN
) src
pivot
(
sum(searches)
for month in ([12],[11],[10])
) piv
)
SELECT *
FROM Pivoted
ORDER BY Dec