Todays latest and yesterdays latest record - sql

I have a table with code and Date
Code Date
----------------------------
A1 21 May 2015 15:47
A2 21 May 2015 10:30
A3 20 May 2015 10:30
A4 21 May 2015 10:30
A1 19 May 2015 15:20
A2 21 May 2015 12:30
A3 19 May 2015 05:30
A4 18 May 2015 15:38
A1 19 May 2015 05:30
A2 20 May 2015 05:30
A3 21 May 2015 05:30
A4 21 May 2015 05:30
A3 21 May 2015 06:30
A1 21 May 2015 05:30
I need to get the Todays latest record, and yesterdays latest record of A1,A2,A3,A4 showing like below
Flag Code Date
-----------------------------------------
Today A1 21 May 2015 15:47
Today A2 21 May 2015 10:30
Today A3 21 May 2015 06:30
Today A4 21 May 2015 10:30
Yesterday A1 --
Yesterday A2 20 May 2015 05:30
Yesterday A3 20 May 2015 10:30
Yesterday A4 --
Help me how to write query to get data

This seems to give your expected output, including the two "dashed" results for yesterday:
declare #t table (Code char(2),[Date] datetime)
insert into #t(Code,Date) values
('A1','2015-05-21T15:47:00'),
('A2','2015-05-21T10:30:00'),
('A3','2015-05-20T10:30:00'),
('A4','2015-05-21T10:30:00'),
('A1','2015-05-19T15:20:00'),
('A2','2015-05-21T12:30:00'),
('A3','2015-05-19T05:30:00'),
('A4','2015-05-18T15:38:00'),
('A1','2015-05-19T05:30:00'),
('A2','2015-05-20T05:30:00'),
('A3','2015-05-21T05:30:00'),
('A4','2015-05-21T05:30:00'),
('A3','2015-05-21T06:30:00'),
('A1','2015-05-21T05:30:00')
;With Dated as (
select *,DATEADD(day,DATEDIFF(day,0,[Date]),0) as BetterDate
from #t
), Numbered as (
select *,ROW_NUMBER() OVER (
PARTITION BY Code,BetterDate
ORDER BY [Date] desc) as rn
from Dated
), Codes as (
select distinct Code from #t
)
select
'Today' as Occasion,
c.Code,
COALESCE(CONVERT(varchar(20),n1.Date),'-') as Date
from
Codes c
left join
Numbered n1
on
c.Code = n1.Code and
n1.rn = 1 and
n1.BetterDate = DATEADD(day,DATEDIFF(day,0,GETDATE()),0)
union all
select
'Yesterday',
c.Code,
COALESCE(CONVERT(varchar(20),n1.Date),'-') as Date
from
Codes c
left join
Numbered n1
on
c.Code = n1.Code and
n1.rn = 1 and
n1.BetterDate = DATEADD(day,DATEDIFF(day,0,GETDATE()),-1)
order by Occasion,Code
After we set up the sample data, we start constructing the query by way of a few CTEs. The first, Dated, just removes the time portion from the mis-named Date column.
Numbered then assigns row numbers to each result, based on dates and codes.
Codes gets the set of all codes for which we have data, so that we can produce results whether or not a particular code has an entry for today or yesterday.
Finally, we use these CTEs to construct your result set, by way of a UNION ALL
Result:
Occasion Code Date
--------- ---- --------------------
Today A1 May 21 2015 3:47PM
Today A2 May 21 2015 12:30PM
Today A3 May 21 2015 6:30AM
Today A4 May 21 2015 10:30AM
Yesterday A1 -
Yesterday A2 May 20 2015 5:30AM
Yesterday A3 May 20 2015 10:30AM
Yesterday A4 -

select case
when cast([Date] as date) >= cast(getdate() as date) then 'Today'
else 'Yesterday'
end as Flag
, Code
, Date
from (
select row_number() over (
partition by Code, cast([Date] as date)
order by [Date] desc) rn
, *
from YourTable
where cast([Date] as date) > dateadd(day, -1, cast(getdate() as date))
) as SubQueryAlias
where rn = 1
Example at SQL Fiddle.

Try this:
SELECT f.Name, c.code, MAX(y.[Date]) AS [Date]
FROM (SELECT -1 ID, 'yesterday' Name
UNION ALL
SELECT 0, 'today') f
CROSS JOIN
(SELECT code
FROM yourTable
GROUP BY code) c
LEFT OUTER JOIN
yourTable y ON c.code = y.code AND DATEDIFF(DAY, GETDATE(), y.[Date]) = f.ID
WHERE
ISNULL(DATEDIFF(DAY, GETDATE(), y.[Date]), 0) > -2
GROUP BY
f.Name, c.code, ISNULL(DATEDIFF(DAY, GETDATE(), y.[Date]), 0)

Here is some code:
DECLARE #t TABLE(Code CHAR(2), Date DATETIME)
INSERT INTO #t
VALUES ( 'A1', '21 May 2015 15:47' ),
( 'A2', '21 May 2015 10:30' ),
( 'A3', '20 May 2015 10:30' ),
( 'A4', '21 May 2015 10:30' ),
( 'A1', '19 May 2015 15:20' ),
( 'A2', '21 May 2015 12:30' ),
( 'A3', '19 May 2015 05:30' ),
( 'A4', '18 May 2015 15:38' ),
( 'A1', '19 May 2015 05:30' ),
( 'A2', '20 May 2015 05:30' ),
( 'A3', '21 May 2015 05:30' ),
( 'A4', '21 May 2015 05:30' ),
( 'A3', '21 May 2015 06:30' ),
( 'A1', '21 May 2015 05:30' )
;WITH codes AS(SELECT DISTINCT Code, d FROM #t
CROSS JOIN (VALUES(CAST(GETDATE() AS DATE)),
(CAST(DATEADD(dd, -1, GETDATE()) AS DATE)))d(d))
SELECT CASE WHEN DAY(GETDATE()) = DAY(Date) THEN 'Today' ELSE 'Yestarday' END Day ,
c.Code ,
MAX(Date) AS Date
FROM codes c
LEFT JOIN #t t ON t.Code = c.Code AND CAST(t.Date AS DATE) = c.d
WHERE Date IS NULL OR Date > CAST(DATEADD(dd, -1, GETDATE()) AS DATE)
GROUP BY c.Code , DAY(Date)
ORDER BY Day, Code
Output:
Day Code Date
Today A1 2015-05-21 15:47:00.000
Today A2 2015-05-21 12:30:00.000
Today A3 2015-05-21 06:30:00.000
Today A4 2015-05-21 10:30:00.000
Yestarday A1 NULL
Yestarday A2 2015-05-20 05:30:00.000
Yestarday A3 2015-05-20 10:30:00.000
Yestarday A4 NULL

Related

Grouping of records hour by hour in oracle

My o/p of the query is like below
Date Hour Orders
2018-02-22 00 22
2018-02-22 03 12
2018-02-22 04 12
2018-02-22 08 12
But I want it like this
Date Hour Orders
2018-02-22 00 22
2018-02-22 01 0
2018-02-22 02 0
2018-02-22 03 12
2018-02-22 04 12
2018-02-22 05 0
2018-02-22 06 0
2018-02-22 07 0
2018-02-22 08 12
Even though there is no order placed during that hour, that hour should be displayed and it should show order placed as 0.
Another way to do that is to use oracle "data-densification" method. The key thing is to add partition by clause after outer join like below.
With Needed_Hours (hr) as (
select lpad(level-1, 2, '0')
from dual
connect by level <= (
select max(to_number(Hour)) - min(to_number(Hour)) + 1
from your_table
)
)
select t."Date", h.hr hour, nvl(t.orders, 0)orders
from Needed_Hours h
left join your_table t partition by (t."Date")
on h.hr = t.hour;
Assuming you have timestamp data(namely dt), split into two columns as date and hour and apply outer join among the data set derived through row generation by difference of extremum hours and the original table(namely t) such as
WITH t1 AS
(
SELECT level-1 AS lvl
FROM dual
CONNECT BY level <= ( SELECT EXTRACT(HOUR FROM MAX(dt) - MIN(dt))+1 FROM t )
), t2 AS
(
SELECT TRUNC(dt) AS "Date", EXTRACT( HOUR FROM dt ) AS hr, orders FROM t
)
SELECT MAX("Date") OVER (ORDER BY lvl ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW ) AS "Date",
lvl AS "Hour", NVL(orders,0) AS "Orders"
FROM t1
LEFT JOIN t2
ON t2.hr = t1.lvl
ORDER BY t1.lvl
Demo

SQL query to produce month report status

I have a table like this:
TICKET ID | OPEN DATE | CLOSE DATE
----------+------------+------------
1 | 2018-12-30 | 2019-01-01
2 | 2019-01-30 | 2019-02-01
3 | 2019-01-20 | 2019-01-22
I have to produce a view that create for each ticket id several entry with a status (OPEN, CLOSED, BACKLOG), for each month of the current year. Status OPEN if OPEN DATE is in the month, status CLOSED if closed date is in the month, BACKLOG if the open date was in the previous month and close date was not in the previous month.
So in the example below the output table would be:
TICKET ID | MONTH | STATUS
----------+--------+---------
1 | JAN-19 | BACKLOG
1 | JAN-19 | CLOSED
2 | JAN-19 | OPEN
3 | JAN-19 | OPEN
3 | JAN-19 | CLOSED
2 | FEB-19 | CLOSED
Is there a way to do that in pure SQL in SQL Server?
Your description suggests:
select t.ticket_id, v.mon, v.status
from t cross apply
(values ('OPEN', datefromparts(year(open_date), month(open_date), 1)),
('CLOSED', datefromparts(year(open_date), month(open_date), 1)),
('BACKLOG', datefromparts(year(dateadd(month, 1, open_date)), month(dateadd(month, 1, open_date)), 1))
) v(status, mon)
where status <> 'BACKLOG' or
datediff(month, open_date, close_date) > 0;
You can add filtering in the where clause for a particular period of time, such as dates in 2019.
This should do exactly what you want for "BACKLOG":
BACKLOG if the open date was in the previous month and close date was not in the previous month.
However, you have not taken multiple months of backlog into account. If this is a possibility, ask a new question, with appropriate sample data and desired results.
Does this do what you want? You can adjust the period it runs over by tweaking the dates table.
declare #tickets table (ticketId int, openDate datetime, closeDate datetime)
insert into #tickets (ticketId, openDate, closeDate) values (1, '30 Dec 2018', '1 Jan 2019')
insert into #tickets (ticketId, openDate, closeDate) values (2, '30 Jan 2019', '1 Feb 2019')
insert into #tickets (ticketId, openDate, closeDate) values (3, '20 Jan 2019', '22 Jan 2019')
; with dates as (
select cast('1 Jan 2019' as datetime) startMon, cast('31 Jan 2019' as dateTime) endMon
union all
select dateAdd(mm, 1, startMon), DATEADD(dd, -1, dateAdd(mm, 2, startMon)) from dates where startMon < '01 Dec 2019'
)
select ticketId, startMon, 'BACKLOG' [Status] from #tickets t inner join dates d on t.openDate between dateAdd(m, -1, startMon) and startMon and closeDate < endMon and closeDate >= startMon
union
select ticketId, startMon, 'OPEN' from #tickets t inner join dates d on t.openDate between startMon and endMon
union
select ticketId, startMon, 'CLOSED' from #tickets t inner join dates d on t.closeDate between startMon and endMon
order by startMon, ticketId

I want to split a row into multiple row based on month and year of the date in SQL Server

For example- I have below rows in my table:
id StartDate EndDate
101 1/03/2017 15/03/2017
102 27/03/2017 10/04/2017
103 25/12/2017 5/02/2018
I want the following output:
id month year
101 03 2017
102 03 2017
102 04 2017
103 12 2017
103 01 2018
103 02 2018
I have tried my best to find a solution but couldn't get through it. Any kind of help is always appreciated.
Thanks in advance.
If i correctly understood your problem then below query will work for you, this is giving the exact output required by you :
DECLARE #SAMPLE_DATA TABLE(id INT, StartDate DATETIME, EndDate DATETIME)
INSERT INTO #SAMPLE_DATA VALUES
(101, '03/1/2017', '03/15/2017'),
(102, '03/27/2017', '04/10/2017'),
(103, '12/25/2017', '02/5/2018')
;WITH SAMPLE_DATA
AS
(
SELECT ID,StartDate FROM #SAMPLE_DATA
UNION ALL
SELECT S1.id,DATEADD(D,1,S.STARTDATE) FROM SAMPLE_DATA S JOIN #SAMPLE_DATA S1 ON S.id=S1.id WHERE
DATEADD(D,1,S.STARTDATE)<=S1.EndDate
)
SELECT DISTINCT ID,MONTH(StartDate)[MONTH],YEAR(StartDate)[YEAR] FROM SAMPLE_DATA ORDER BY ID,YEAR,MONTH
Output of query :
-------------------
ID MONTH YEAR
-------------------
101 3 2017
102 3 2017
102 4 2017
103 12 2017
103 1 2018
103 2 2018
-------------------
You could try it with CTE
DECLARE #SampleDate AS TABLE (
Id int, StartDate date, EndDate date
)
INSERT INTO #SampleDate
(
Id,
StartDate,
EndDate
)
VALUES (101, '2017-03-01', '2017-03-15') ,
(102, '2017-03-27', '2017-04-10'),
(103, '2017-12-25', '2018-02-05')
DECLARE #MinDate date = '2017-01-01'
DECLARE #MaxDate date = '2020-01-01'
;WITH temp AS
(
SELECT #MinDate AS StartMonth, EOMOnth(#MinDate) AS EndMonth
UNION ALL
SELECT Dateadd(month, 1, t.StartMonth), Dateadd(month, 1, t.EndMonth) AS CurrentDate
FROM temp t
WHERE t.EndMonth < #MaxDate
)
SELECT DISTINCT sd.Id, DATEPART(Month,t.StartMonth) AS Month,
DATEPART(Year,t.StartMonth) AS Year
FROM temp t
INNER JOIN #SampleDate sd ON t.StartMonth BETWEEN sd.StartDate AND sd.EndDate
OR t.EndMonth BETWEEN sd.StartDate AND sd.EndDate
OPTION (MAXRECURSION 0)
Demo link: RexTester

SQL Query building: howto decompose periods of time in different rows

How can I build a SQL Query to decompose some periods, for example in months.
database table:
id fromdate todate value
--------------------------------------------
100 01.01.2015 01.03.2015 10
desired query result:
id fromdate todate value
--------------------------------------------
100 01.01.2015 01.02.2015 5,25
100 01.02.2015 01.03.2015 4,75
where value is based on days between the 2 dates, for example:
value(january) = 31(january nr of days) * 10(original value) / 59(total days) = 5,25
Thank you
For calculations like this you can use date dimension - a table that contains all the dates in your domain as single rows (see this for example).
Once you have date dimension in your database things become simple:
WITH data_by_date AS
( -- Here we join dates to your periods to turn each row in
-- as many rows as there are days in the period.
-- We also turn value field into value_per_day.
SELECT
d.date,
d.month_year,
t.id,
value / (t.todate - t.fromdate) as value_per_day
FROM
dim_date d INNER JOIN
my_table t ON d.date >= t.fromdate AND d.date < t.todate
)
SELECT -- Here we group by results by month.
dd.id,
MIN(dd.date) as fromdate,
MAX(dd.date) as todate,
SUM(dd.value_per_day) as value
FROM data_by_date dd
GROUP BY dd.id, dd.month_year
Use a hierarchical query to generate a list of months for each entry:
SQL Fiddle
Oracle 11g R2 Schema Setup:
CREATE TABLE TEST (id, fromdate, todate, value ) AS
SELECT 100, DATE '2015-01-01', DATE '2015-03-01', 10 FROM DUAL
UNION ALL SELECT 200, DATE '2014-12-22', DATE '2015-01-06', 30 FROM DUAL
Query 1:
SELECT ID,
fromdate,
todate,
VALUE * ( todate - fromdate ) / ( maxdate - mindate ) AS value
FROM (
SELECT ID,
GREATEST( t.fromdate, m.COLUMN_VALUE ) AS fromdate,
LEAST( t.todate, ADD_MONTHS( m.COLUMN_VALUE, 1 ) ) AS todate,
t.fromdate AS mindate,
t.todate AS maxdate,
t.value
FROM TEST t,
TABLE(
CAST(
MULTISET(
SELECT ADD_MONTHS( TRUNC( t.fromdate, 'MM' ), LEVEL - 1 )
FROM DUAL
CONNECT BY
ADD_MONTHS( TRUNC( t.fromdate, 'MM' ), LEVEL - 1 ) < t.todate
)
AS SYS.ODCIDATELIST
)
) m
)
Results:
| ID | FROMDATE | TODATE | VALUE |
|-----|----------------------------|----------------------------|-------------------|
| 100 | January, 01 2015 00:00:00 | February, 01 2015 00:00:00 | 5.254237288135593 |
| 100 | February, 01 2015 00:00:00 | March, 01 2015 00:00:00 | 4.745762711864407 |
| 200 | December, 22 2014 00:00:00 | January, 01 2015 00:00:00 | 20 |
| 200 | January, 01 2015 00:00:00 | January, 06 2015 00:00:00 | 10 |
Use function add_months() and hierarchical subquery to generate periods for each id:
select id, d1, d2, round(value*(d2-d1)/nod, 2) value
from (
select id, value, todate-fromdate nod, add_months(fromdate, level-1) d1,
least(add_months(fromdate, level), todate) d2
from data
connect by add_months(fromdate, level) <= trunc(add_months(todate, 1)-1)
and id = prior id and prior dbms_random.value is not null )
SQLFiddle demo

Generating dates between two dates

I need to generate all dates between two given dates. This works fine as long as there is just one date range. However, if I have multiple date ranges, this solution doesn't work. I have searched here as well as on asktom, but couldn't locate relevant pointers/solution.
I tried both the solutions using all_objects and CONNECT BY ROWNUM, but no luck. Here is the problem statement: sqlfiddle
Input
ID START_DATE END_DATE
101 April, 01 2013 April, 10 2013
102 May, 10 2013 May, 12 2013
Output
ID Dates
101 April, 01 2013
101 April, 02 2013
101 April, 03 2013
101 April, 04 2013
101 April, 05 2013
101 April, 06 2013
101 April, 07 2013
101 April, 08 2013
101 April, 09 2013
101 April, 10 2013
102 May, 10 2013
102 May, 11 2013
102 May, 12 2013
select
A.ID,
A.START_DATE+delta dt
from
t_dates A,
(
select level-1 as delta
from dual
connect by level-1 <= (
select max(end_date - start_date) from t_dates
)
)
where A.START_DATE+delta <= A.end_date
order by 1, 2
Please try:
select
distinct ID,
START_DATE+level-1 DATES
from dual a, TABLE_DATES b
connect by level <= (END_DATE-START_DATE)+1
order by ID;
select g.cycle_dt
from
(select to_date(d,'DD-MM-YYYY') cycle_dt
from dual
model
dimension by (trunc(to_date('30092015', 'DDMMYYYY')) d)
measures (0 y)
rules (
y[for d from trunc(to_date('30092015', 'DDMMYYYY')) to to_date('30102015', 'DDMMYYYY') increment 1]=0
)) g
order by g.cycle_dt;
WITH NUMS AS (
SELECT LEVEL-1 DaysToAdd
FROM DUAL
CONNECT BY LEVEL <= (
SELECT MAX(END_DATE - START_DATE + 1)
FROM HOLIDAY)
)
SELECT START_DATE + DaysToAdd DATE_TIME, DESCRIPTION, IS_SCHOOL_HOLIDAY, UPDATE_BY, CREATE_DATE
FROM HOLIDAY
CROSS JOIN NUMS
WHERE HOLIDAY.END_DATE - HOLIDAY.START_DATE > DaysToAdd
ORDER BY 1, 2;
SELECT TO_DATE('1-JAN-21', 'dd-mm-yy') + ROWNUM - 1
FROM DUAL
CONNECT BY LEVEL <= (TO_DATE('26-APR-22', 'dd-mm-yy') -
TO_DATE('1-JAN-21', 'dd-mm-yy')) + 1;