Spread a table in a date time interval - sql

Hello everyone it's been some days that I use sql to make analysis and I meet all kinds of problems that I solves thanks to your forum.
Now I'd like to create a view that recuperates the interval of time and shows in detail the dates in this interval.
I have the following table:
And I want to create the view that displays the result:
For example in the player1 MyTable to play five days from 01/01/2012
to 05/01/2012. So the view displays 5 lines for player1 with the date 01/01/2012, 02/01/2012, 03/01/2012, 04/01/2012, 05/01/2012.
Thank you in advance for your help.

You have to create a common table expression that give you the date range ( i have created a date range of the current month but you can choice another range) :
WITH DateRange(dt) AS
(
SELECT CONVERT(datetime, '2012-01-01') dt
UNION ALL
SELECT DATEADD(dd,1,dt) dt FROM DateRange WHERE dt < CONVERT(datetime, '2012-01-31')
)
SELECT dates.dt AS DatePlaying, PlayerName
FROM MyTable t
JOIN DateRange dates ON dt BETWEEN t.BeginDate AND t.DateEnd
ORDER BY PlayerName, DatePlaying

Another approach to this is simply to create an enumeration table to add values to dates:
with enumt as (select row_number() over (order by (select NULL)) as seqnum
from mytable
)
select dateadd(d, e.seqnum, mt.DateBegin) as DatePlaying, mt.PlayerName
from MyTable mt join
enum e
on enumt.seqnum <= e.NumberOfPlayingDay
The only purpose of the "with" clause is to generate a sequence of integers starting at 1.

Related

Stretch the table of balances, for all dates of the calendar

I have a table of stock balances, as of the date of their change
SQLFiddle
I need to stretch these Remains to missing dates including 0 leftovers
I created a calendar
CREATE TABLE Calendar
("Date" DATE)
GO
DECLARE #start datetime
DECLARE #end datetime
SET #start = (SELECT MIN (a.Date) FROM Remains a)
SET #end = GETDATE();
WITH cte AS
(
SELECT #start "Date"
UNION all
SELECT "Date" + 1
FROM cte
WHERE "Date" < #end
)
INSERT INTO Calendar
SELECT cast("Date" AS date) AS "Date"
FROM cte
WHERE "Date" < GETDATE()
option(MAXRECURSION 0)
Where I take the minimum date from the Remains table and drag it until today
Calendar
Next, I join the Calendar to the table of Remains using OUTER APPLY,
SELECT
b.Date
,x.W_Code
,x.Prod_Code
,x.Quality
,x.Count
FROM Calendar b
OUTER APPLY (
SELECT
a.Date
,a.W_Code
,a.Prod_Code
,a.Quality
,a.Count
FROM Remains a
WHERE a.Date = b.Date AND a.Prod_Code = N'00005026957' AND a.W_Code = N'000000017' ) x
and if my query applies to 1 specific warehouse, product and quality, then I get the desired result
Result
But if I remove the condition for the warehouse and the product, the table built incorrectly
I need for each group W_Code, Prod_Code, Quality
have each date from the Calendar
Please help me to find a way to implement this
Maybe I don't need a Calendar
I read about recursive CTE but did not understand how to apply it
How to fill the table with values further, I know, the problem is in the correct joining of the table with dates
Thanks

Last day of month joined to second table

I need to get the last day of the previous month and then join this to another table to return the year/month column that the date relates to but I'm struggling to achieve what I want.
I have tried:
SELECT b.yrmonth, LAST_DAY(ADD_MONTHS(SYSDATE,-1)) DT
FROM dual a
INNER JOIN D_DAY b on DT = b.DT
The year month just returns everything in the table rather than just one row so any help would be much appreciated!
Your query is effectively:
SELECT b.yrmonth,
'some constant masking b.DT' DT
FROM dual a
INNER JOIN
D_DAY b
on ( b.DT = b.DT ) -- Always true
You do not need to join the DUAL table and need to filter your table in the WHERE clause.
If the DT date column has varying time components:
SELECT yrmonth, dt
FROM D_DAY
WHERE DT >= TRUNC(LAST_DAY(ADD_MONTHS(SYSDATE,-1)))
AND DT < TRUNC(SYSDATE,'MM');
(Which will allow the database to use indexes on the DT column)
or, if your DT column always has dates with the time component at midnight:
SELECT yrmonth, dt
FROM D_DAY
WHERE DT = TRUNC(LAST_DAY(ADD_MONTHS(SYSDATE,-1)));
You don't need to join with dual table. You can simply add your condition in thewhere clause:
select *
from D_DAY b
where TRUNC(b.DT) = TRUNC(LAST_DAY(ADD_MONTHS(SYSDATE,-1))
and cast the date (with time) as date because LAST_DAY function returns date (with time component and if you want to check the date, you need to cast it before.

force number of rows to return in date range from SQL query

I'm running a query on our SQL (2012) database which returns a count of records in a given date range, grouped by the date.
For example:
Date Count
12/08 12
14/08 19
19/08 11
I need to fill in the blanks as the charts I plot get screwed up because there are missing values. Is there a way to force the SQL to report back a blank row, or a "0" value when it doesn't come across a result?
My query is
SELECT TheDate, count(recordID)
FROM myTable
WHERE (TheDate between '12-AUG-2013 00:00:00' and '20-AUG-2013 23:59:59')
GROUP BY TheDate
Would I need to create a temp table with the records in, then select from that and right join any records from myTable?
Thanks for any help!
If you create a (temporary or permanent) table of the date range, you can then left join to your results to create a result set including blanks
SELECT dates.TheDate, count(recordID)
FROM
( select
convert(date,dateadd(d,number,'2013-08-12')) as theDate
from master..spt_values
where type='p' and number < 9
) dates
left join yourtable on dates.thedate = convert(date,yourtable.thedate)
GROUP BY dates.TheDate
A temp table would do the job but for such a small date range you could go even simpler and use a UNION-ed subquery. E.g:
SELECT dates.TheDate, ISNULL(counts.Records, 0)
FROM
(SELECT TheDate, count(recordID) AS Records
FROM myTable
WHERE (TheDate between '12-AUG-2013 00:00:00' and '20-AUG-2013 23:59:59')
GROUP BY TheDate
) counts
RIGHT JOIN
(SELECT CAST('12-AUG-2013' AS DATETIME) AS TheDate
UNION ALL SELECT CAST('13-AUG-2013' AS DATETIME) AS TheDate
UNION ALL SELECT CAST('14-AUG-2013' AS DATETIME) AS TheDate
UNION ALL SELECT CAST('15-AUG-2013' AS DATETIME) AS TheDate
UNION ALL SELECT CAST('16-AUG-2013' AS DATETIME) AS TheDate
UNION ALL SELECT CAST('17-AUG-2013' AS DATETIME) AS TheDate
UNION ALL SELECT CAST('18-AUG-2013' AS DATETIME) AS TheDate
UNION ALL SELECT CAST('19-AUG-2013' AS DATETIME) AS TheDate
UNION ALL SELECT CAST('20-AUG-2013' AS DATETIME) AS TheDate
) dates
ON counts.TheDate = dates.TheDate
Here's a SQL Fiddle Demo.
If you need a more generic (but also more complex) solution, take a look at this excellent answer (by #RedFilter) to a similar question.

Query for dates which are not present in a table

Consider a table ABC which has a column of date type.
How can we get all the dates of a range (between start date and end date) which are not present in the table.
This can be done in PLSQL.I am searching a SQL query for it.
You need to generate the arbitrary list of dates that you want to check for:
http://hashfactor.wordpress.com/2009/04/08/sql-generating-series-of-numbers-in-oracle/
e.g.:
-- generate 1..20
SELECT ROWNUM N FROM dual
CONNECT BY LEVEL <= 20
Then left join with your table, or use a where not exists subquery (which will likely be faster) to fetch the dates amongst those you've generated that contains no matching record.
Assuming that your table's dates do not include a time element (ie. they are effectively recorded as at midnight), try:
select check_date
from (select :start_date + level - 1 check_date
from dual
connect by level <= 1 + :end_date - :start_date) d
where not exists
(select null from mytable where mydate = check_date)
Given a date column in order to do this you need to generate a list of all possible dates between the start and end date and then remove those dates that already exist. As Mark has already suggested the obvious way to generate the list of all dates is to use a hierarchical query. You can also do this without knowing the dates in advance though.
with the_dates as (
select date_col
from my_table
)
, date_range as (
select max(date_col) as maxdate, min(date_col) as mindate
from the_dates
)
select mindate + level
from date_range
connect by level <= maxdate - mindate
minus
select date_col
from the_dates
;
Here's a SQL Fiddle
The point of the second layer of the CTE is to have a "table" that has all the information you need but is only one row so that the hierarchical query will work correctly.

How to count records for each day in a range (including days without records)

I'm trying to refine this question a little since I didn't really ask correctly last time. I am essentially doing this query:
Select count(orders)
From Orders_Table
Where Order_Open_Date<=To_Date('##/##/####','MM/DD/YYYY')
and Order_Close_Date>=To_Date('##/##/####','MM/DD/YYYY')
Where ##/##/#### is the same day. In essence this query is designed to find the number of 'open' orders on any given day. The only problem is I'm wanting to do this for each day of a year or more. I think if I knew how to define the ##/##/#### as a variable and then grouped the count by that variable then I could get this to work but I'm not sure how to do that-or there may be another way as well. I am currently using Oracle SQL on SQL developer. Thanks for any input.
You could use a "row generator" technique like this (edited for Hogan's comments):
Select RG.Day,
count(orders)
From Orders_Table,
(SELECT trunc(SYSDATE) - ROWNUM as Day
FROM (SELECT 1 dummy FROM dual)
CONNECT BY LEVEL <= 365
) RG
Where RG.Day <=To_Date('##/##/####','MM/DD/YYYY')
and RG.Day >=To_Date('##/##/####','MM/DD/YYYY')
and Order_Open_Date(+) <= RG.Day
and Order_Close_Date(+) >= RG.Day - 1
Group by RG.Day
Order by RG.Day
This should list each day of the previous year with the corresponding number of orders
Lets say you had a table datelist with a column adate
aDate
1/1/2012
1/2/2012
1/3/2012
Now you join that to your table
Select *
From Orders_Table
join datelist on Order_Open_Date<=adate and Order_Close_Date>=adate
This gives you a list of all the orders you care about, now you group by and count
Select aDate, count(*)
From Orders_Table
join datelist on Order_Open_Date<=adate and Order_Close_Date>=adate
group by adate
If you want to pass in a parameters then just generate the dates with a recursive cte
with datelist as
(
select #startdate as adate
UNION ALL
select adate + 1
from datelist
where (adate + 1) <= #lastdate
)
Select aDate, count(*)
From Orders_Table
join datelist on Order_Open_Date<=adate and Order_Close_Date>=adate
group by adate
NOTE: I don't have an Oracle DB to test on so I might have some syntax wrong for this platform, but you get the idea.
NOTE2: If you want all dates listed with 0 for those that have nothing use this as your select statement:
Select aDate, count(Order_Open_Date)
From Orders_Table
left join datelist on Order_Open_Date<=adate and Order_Close_Date>=adate
group by adate
If you want only one day you can query using TRUNC like this
select count(orders)
From orders_table
where trunc(order_open_date) = to_date('14/05/2012','dd/mm/yyyy')