How to Find Week Number in look up Table - sql

I have a Two Tables
One is Lookup Table Week for WeekNumber and Weekdate
And Another is Register
What I want to Return is current WeekLabel from the Week table where Entrytime is falling between two dates
For example :
Entrytime 2016-08-15 09:02:15.0000000
is between Weekstartdate value 2016-08-15 00:00:00.000 and 2016-08-22 00:00:00.000
so I want to return WeekLabel 1 and WeekStartdate 2016-08-15 from week table and so on..

Using cross apply() to get the latest WeekStartDate that occurs before or on EntryTime.
select r.*
, x.WeekLabel
from register r
cross apply (
select top 1 w.WeekLabel
from [Week] w
where w.weekstartdate =< r.entrytime
order by w.weekstartdate desc
) as x
switch to outer apply() for left join functionality, e.g. return null if there is no corresponding WeekLabel.

You can join the tables based on the entrytime being greater than the weekstartdate; this will multiply each entry for all the preceding weeks, so you can get the latest one with a max on WeekLabel (assuming they're constantly increasing).
select t2.ID, t2.CODE, max(t1.WeekLabel)
from Week t1
join Register t2
on t2.Entrytime > t1.Weekstartdate
group by t2.ID, t2.CODE

Another option is a simple JOIN
Select R.*
,W.WeekLabel
,W.WeekStartdate
From Register R
Join [Week] W
on R.EntryTime >= W.WeekStartDate
and R.EntryTime < DateAdd(DAY,7,W.WeekStartDate)

Another option is to join to the week reference table using the datediff function:
SELECT * from
register r
join [Week] w on
DATEDIFF(day, r.entryTime, w.weekStartDate) between 0 and 7

Related

How to insert values based on another column value

Below is a subset of my table (for the first id)
date
id
value
01/01/2022
1
5
08/01/2022
1
2
For each id, the dates are not consecutive (e.g., for id 1, the min date is 01/01/2022 and the max date is 08/01/2022)--there are 7 days in between both dates. I want to insert rows to make the dates for each id consecutive and contiguous - the value for the value field/column to be filled with 0s so that the updated table looks like:
date
id
value
01/01/2022
1
5
02/01/2022
1
0
03/01/2022
1
0
04/01/2022
1
0
05/01/2022
1
0
06/01/2022
1
0
07/01/2022
1
0
08/01/2022
1
2
Any SQL code on how to implement this would be highly appreciated. I have a calendar table but am unsure how to join it with the above table so that I fill in missing dates dynamically for each id with 0s.
My calendar table looks like:
date
01/01/2022
02/01/2022
03/01/2022
04/01/2022
Considering you state you have a calendar table, it seems what you need to do with JOIN to it with the MIN and MAX dates from your other table, and the LEFT JOIN back to your table:
WITH MinMax AS(
SELECT ID,
MIN(date) AS MinDate,
MAX(date) AS MaxDate
FROM dbo.YourTable
GROUP BY ID),
Dates AS(
SELECT MM.ID,
C.CalendarDate AS [Date]
FROM MinMax MM
JOIN dbo.CalendarTable C ON MM.MinDate <= C.CalendarDate
AND MM.MaxDate >= C.CalendarDate)
SELECT D.ID,
D.[Date],
ISNULL(YT.[Value],0) AS [Value]
FROM Dates D
LEFT JOIN dbo.YourTable YT ON D.ID = YT.ID
AND D.[Date] = YT.[Date];
SET DATEFORMAT DMY
-- CREATE A TABLE WITH OUR INPUT DATA
DROP TABLE IF EXISTS #TheData
GO
CREATE TABLE #TheData
(TheDate DATE, id INT, TheValue INT)
INSERT INTO #TheData
(TheDate,id,Thevalue)
VALUES
('01/01/2022',1,5),
('08/01/2022',1,2),
('17/01/2022',2,7),
('25/01/2022',2,7),
('15/02/2022',2,7)
-- CREATE A CALENDAR CTE
DECLARE #StartDate date = '20210101';
DECLARE #CutoffDate date = DATEADD(DAY, -1, DATEADD(YEAR, 2, #StartDate));
;WITH DateSeq(TheDate) AS
(
SELECT #StartDate
UNION ALL
SELECT DATEADD(dd,1,TheDate) FROM DateSeq
WHERE TheDate < #CutoffDate
)
-- CROSS JOIN OUR CALENDAR CTE TO OUR SOURCE DATA. DERIVED TABLE TO GET FIRST AND LAST OF EACH RANGE TO USE FOR JOIN
SELECT
ds.*
,SourceDataRangesByID.ID
,ISNULL(td.TheValue,0) AS TheValue
FROM
DateSeq ds
CROSS JOIN
(
SELECT
d.ID
,MIN(d.TheDate) AS MinDatePerID
,MAX(d.TheDate) AS MaxDatePerID
FROM #TheData d
GROUP BY d.ID
) SourceDataRangesByID
LEFT JOIN #TheData td ON td.id = SourceDataRangesByID.ID AND td.TheDate = ds.TheDate
WHERE ds.TheDate >= SourceDataRangesByID.MinDatePerID
AND ds.TheDate <= SourceDataRangesByID.MaxDatePerID
OPTION (MAXRECURSION 0);
try the generate_series to create a date table then right join with it and coalesce for the non null value
SELECT generate_series('2016-01-01', -- series start date
'2018-06-30', -- series end date
'1 day'::interval)::date AS day) AS daily_series
from mytable
See Generate_Series for TSQL
https://dba.stackexchange.com/questions/255165/does-ms-sql-server-have-generate-series-function
(Sql server 2022)
https://learn.microsoft.com/en-us/sql/t-sql/functions/generate-series-transact-sql?view=sql-server-ver16

Return row with 0 for dates which has no entry in table - SQL

I have a table that records daily sales data. However, there are days when no sale is made and hence there is no record on the database for those dates. Is it possible to extract data out from the table that returns null for these dates when no sale was made
Referring to the image attached, it is seen there is no sales done on Jan 4 and Jan 8. I would like to write a SQL query that would return all dates from Jan 1 - Jan 10 but for Jan 4 and Jan 8, it should return 0 since there is no row for those dates (no sale done)
My date starts from Mar 1, 2018 and should go on for the next few quarters.
Yes. In Postgres, you can use generate_series() to generate dates or numbers within a range.
Then, you can use a cross join to generate the rows and then a left join to bring in the data:
select s.seller, gs.dte, t.count
from (select generate_series(mindate::timestamp, maxdate::timestamp, interval '1 day')::date
from (select min(date) as mindate, max(date) as maxdate
from t
) x
) gs(dte) cross join
(select distinct seller from t) s left join
t
on t.date = gs.dte and t.seller = s.seller
CTE is also an alternative here,
DECLARE #FDATE DATE = '2018-01-01'
,#TDATE DATE = '2018-01-10'
;WITH CTE_DATE
AS (
SELECT #FDATE AS CDATE
UNION ALL
SELECT DATEADD(DAY,1,CDATE)
FROM CTE_DATE
WHERE DATEADD(DAY,1,CDATE) <= #TDATE
)
SELECT C.CDATE AS [DATE],COUNT(*) AS [COUNT]
FROM CTE_DATE AS C
LEFT OUTER JOIN [MY_TABLE] AS M ON C.CDATE = M.[DATE] --*[your table here]*
GROUP BY C.CDATE
OPTION ( MAXRECURSION 0 );

How to select all dates in SQL query

SELECT oi.created_at, count(oi.id_order_item)
FROM order_item oi
The result is the follwoing:
2016-05-05 1562
2016-05-06 3865
2016-05-09 1
...etc
The problem is that I need information for all days even if there were no id_order_item for this date.
Expected result:
Date Quantity
2016-05-05 1562
2016-05-06 3865
2016-05-07 0
2016-05-08 0
2016-05-09 1
You can't count something that is not in the database. So you need to generate the missing dates in order to be able to "count" them.
SELECT d.dt, count(oi.id_order_item)
FROM (
select dt::date
from generate_series(
(select min(created_at) from order_item),
(select max(created_at) from order_item), interval '1' day) as x (dt)
) d
left join order_item oi on oi.created_at = d.dt
group by d.dt
order by d.dt;
The query gets the minimum and maximum date form the existing order items.
If you want the count for a specific date range you can remove the sub-selects:
SELECT d.dt, count(oi.id_order_item)
FROM (
select dt::date
from generate_series(date '2016-05-01', date '2016-05-31', interval '1' day) as x (dt)
) d
left join order_item oi on oi.created_at = d.dt
group by d.dt
order by d.dt;
SQLFiddle: http://sqlfiddle.com/#!15/49024/5
Friend, Postgresql Count function ignores Null values. It literally does not consider null values in the column you are searching. For this reason you need to include oi.created_at in a Group By clause
PostgreSql searches row by row sequentially. Because an integral part of your query is Count, and count basically stops the query for that row, your dates with null id_order_item are being ignored. If you group by oi.created_at this column will trump the count and return 0 values for you.
SELECT oi.created_at, count(oi.id_order_item)
FROM order_item oi
Group by io.created_at
From TechontheNet (my most trusted source of information):
Because you have listed one column in your SELECT statement that is not encapsulated in the count function, you must use a GROUP BY clause. The department field must, therefore, be listed in the GROUP BY section.
Some info on Count in PostgreSql
http://www.postgresqltutorial.com/postgresql-count-function/
http://www.techonthenet.com/postgresql/functions/count.php
Solution #1 You need Date Table where you stored all date data. Then do a left join depending on period.
Solution #2
WITH DateTable AS
(
SELECT DATEADD(dd, 1, CONVERT(DATETIME, GETDATE())) AS CreateDateTime, 1 AS Cnter
UNION ALL
SELECT DATEADD(dd, -1, CreateDateTime), DateTable.Cnter + 1
FROM DateTable
WHERE DateTable.Cnter + 1 <= 5
)
Generate Temporary table based on your input and then do a left Join.

sql count statement with multiple date ranges

I have two table with different appointment dates.
Table 1
id start date
1 5/1/14
2 3/2/14
3 4/5/14
4 9/6/14
5 10/7/14
Table 2
id start date
1 4/7/14
1 4/10/14
1 7/11/13
2 2/6/14
2 2/7/14
3 1/1/14
3 1/2/14
3 1/3/14
If i had set date ranges i can count each appointment date just fine but i need to change the date ranges.
For each id in table 1 I need to add the distinct appointment dates from table 2 BUT only
6 months prior to the start date from table 1.
Example: count all distinct appointment dates for id 1 (in table 2) with appointment dates between 12/1/13 and 5/1/14 (6 months prior). So the result is 2...4/7/14 and 4/10/14 are within and 7/1/13 is outside of 6 months.
So my issue is that the range changes for each record and i can not seem to figure out how to code this.For id 2 the date range will be 9/1/14-3/2/14 and so on.
Thanks everyone in advance!
Try this out:
SELECT id,
(
SELECT COUNT(*)
FROM table2
WHERE id = table1.id
AND table2.start_date >= DATEADD(MM,-6,table1.start_date)
) AS table2records
FROM table1
The DATEADD subtracts 6 months from the date in table1 and the subquery returns the count of related records.
I think what you want is a type of join.
select t1.id, count(t2.id) as numt2dates
from table1 t1 left outer join
table2 t2
on t1.id = t2.id and
t2.startdate between dateadd(month, -6, t1.startdate) and t1.startdate
group by t1.id;
The exact syntax for the date arithmetic depends on the database.
Thank you this solved my issue. Although this may not help you since you are not attempting to group by date. But the answer gave me the insights to resolve the issue I was facing.
I was attempting to gather the total users a date criteria that had to be evaluated by multiple fields.
WITH data AS (
SELECT generate_series(
(date '2020-01-01')::timestamp,
NOW(),
INTERVAL '1 week'
) AS date
)
SELECT d.date, (SELECT COUNT(DISTINCT h.id) AS user_count
FROM history h WHERE h.startDate < d.date AND h.endDate > d.date
ORDER BY 1 DESC) AS total_records
FROM data d ORDER BY d.date DESC
2022-05-16, 15
2022-05-09, 13
2022-05-02, 13
...

SQL to identify missing week

I have a database table with the following structure -
Week_End Sales
2009-11-01 43223.43
2009-11-08 4324.23
2009-11-15 64343.23
...
Week_End is a datetime column, and the date increments by 7 days with each new entry.
What I want is a SQL statement that will identify if there is a week missing in the sequence. So, if the table contained the following data -
Week_End Sales
2009-11-01 43223.43
2009-11-08 4324.23
2009-11-22 64343.73
...
The query would return 2009-11-15.
Is this possible? I am using SQL Server 2008, btw.
You've already accepted an answer so I guess you don't need this, but I was almost finished with it anyway and it has one advantage that the selected solution doesn't have: it doesn't require updating every year. Here it is:
SELECT T1.*
FROM Table1 T1
LEFT JOIN Table1 T2
ON T2.Week_End = DATEADD(week, 1, T1.Week_End)
WHERE T2.Week_End IS NULL
AND T1.Week_End <> (SELECT MAX(Week_End) FROM Table1)
It is based on Andemar's solution, but handles the changing year too, and doesn't require the existence of the Sales column.
Join the table on itself to search for consecutive rows:
select a.*
from YourTable a
left join YourTable b
on datepart(wk,b.Week_End) = datepart(wk,a.Week_End) + 1
-- No next week
where b.sales is null
-- Not the last week
and datepart(wk,a.Week_End) <> (
select datepart(wk,max(Week_End)) from YourTable
)
This should return any weeks without a next week.
Assuming your "week_end" dates are always going to be the Sundays of the week, you could try a CTE - a common table expression that lists out all the Sundays for 2009, and then do an outer join against your table.
All those rows missing from your table will have a NULL value for their "week_end" in the select:
;WITH Sundays2009 AS
(
SELECT CAST('20090104' AS DATETIME) AS Sunday
UNION ALL
SELECT
DATEADD(DAY, 7, cte.Sunday)
FROM
Sundays2009 cte
WHERE
DATEADD(DAY, 7, cte.Sunday) < '20100101'
)
SELECT
sun.Sunday 'Missing week end date'
FROM
Sundays2009 sun
LEFT OUTER JOIN
dbo.YourTable tbl ON sun.Sunday = tbl.week_end
WHERE
tbl.week_end IS NULL
I know this has already been answered, but can I suggest something really simple?
/* First make a list of weeks using a table of numbers (mine is dbo.nums(num), starting with 1) */
WITH AllWeeks AS (
SELECT DATEADD(week,num-1,w.FirstWeek) AS eachWeek
FROM
dbo.nums
JOIN
(SELECT MIN(week_end) AS FirstWeek, MAX(week_end) as LastWeek FROM yourTable) w
ON num <= DATEDIFF(week,FirstWeek,LastWeek)
)
/* Now just look for ones that don't exist in your table */
SELECT w.eachWeek AS MissingWeek
FROM AllWeeks w
WHERE NOT EXISTS (SELECT * FROM yourTable t WHERE t.week_end = w.eachWeek)
;
If you know the range you want to look over, you don't need to use the MIN/MAX subquery in the CTE.