Join query on Date Range - sql

HI
I have following tables
=========================
Periods
=========================
PeriodID StartDate EndDate
1 01-01-11 07-01-11
2 08-01-11 15-01-11
and so on for whole year
=========================
History
=========================
PersonID From To
1 01-01-11 05-04-11
2 17-06-11 NULL
and so on
I want the following output
StartDate EndDate PersonID
01-01-11 07-01-11 1
08-01-11 15-01-11 1
.
.
15-04-11 21-04-11 NULL
.
.
15-06-11 21-06-11 2
I need to take join between these two tables but i couldn't figure how join condition will be look like
Ragards

SELECT
p.StartDate,
p.EndDate,
h.PersonID
FROM Periods p
LEFT JOIN History h
ON h.[From] BETWEEN p.StartDate AND p.EndDate OR
p.StartDate BETWEEN h.[From] AND ISNULL(h.[To], '30000101')

It would affect performance, but I think it is worth just trying the odd looking between:
select x
from table1 t1
inner join table2 t2
on t2.date between t1.startdate and t1.enddate
Whether it works or not will depend on whether this is to be production, or just a one time thing, and how many records are involved. It may be way too slow.

Can you please try:
SELECT P.StartDate, P.EndDate, H.PersonID
FROM Period P INNER JOIN History H ON P.StartDate <= H.Fromand (P.EndDate >= H.To OR H.To IS NULL)
I have edited the SQL after reading the spec more clearly
I have edited the SQL again. I'm using INNER JOIN now.

You need to do a left join in order to show all the periods available even if there are no history entries associated with that period. The criteria would be if the History date was between the period. You would also need to check if the To date was null and include it into your results
SELECT p.StartDate, p.EndDate, h.PersonId
FROM Period p
LEFT JOIN History h
ON p.StartDate >= h.[From] AND
(h.[To] IS NULL OR p.EndDate <= h.[To])

at table 'history', set NULL to '9999-12-31'
select * from periods a inner join history b on a.from < b.to and a.to > b.from

Related

Finding days when users haven't created any entries

I've 2 tables: users and time_entries, time entries has a foreign key to the users table. Users may create time entries with some time amount in it. I want to write a query which could return summarized amounts of time in arbitrary dates range grouped by user and date - it's easy but I need to include also days when nobody entered any time_entry. I've tried to create an additional table called calendar with dates and left join time_entries to it but I couldn't retrieve a list of users that haven't entered any time_entry. Here is my query:
SELECT te.date, SUM(te.amount), user_name
FROM calendar c
LEFT JOIN time_entries te on c.date = te.date
RIGHT JOIN asp_net_users anu on te.user_id = anu.id
GROUP BY user_name, te.date
If you just want the days no user made any entry. you can use NOT EXISTS and a correlated subquery.
SELECT c.date
FROM calendar c
WHERE NOT EXISTS (SELECT *
FROM time_entries te
WHERE te.date = c.date);
If you want all users along with the days they haven't made any entry cross join the users and the days and then also use a NOT EXISTS.
SELECT anu.user_name,
c.date
FROM asp_net_users anu
CROSS JOIN calendar c
WHERE NOT EXISTS (SELECT *
FROM time_entries te
WHERE te.user_id = anu.id
AND te.date = c.date);
Thanks to sticky bit examples I was able to write the following query which solves my problem:
SELECT c.date, a.id, COALESCE(sum(te.amount), 0)
FROM asp_net_users a
CROSS JOIN (SELECT *
FROM calendar
WHERE date BETWEEN '2019-10-01 00:00:00'::timestamp AND '2019-10-31 00:00:00'::timestamp) c
LEFT JOIN time_entries te on a.id = te.user_id AND c.date = te.date
WHERE a.department_guid = '95b7538d-3830-48d7-ba06-ad7c51a57191'
GROUP BY c.date, a.id
ORDER BY c.date

Need to understand specific LEFT OUTER JOIN behavior in SQL SELECT

I have two tables, transactions and dates. One date may have one or more transactions. I need to get a list of dates with or without transactions of a specific account (account number 111 below).
select d.the_date, t.account, t.amount from dates as d
LEFT OUTER JOIN transactions as t ON t.tx_date=d.the_date
where t.account=111 AND d.the_date>='2016-01-02' and d.the_date<='2017-12-30'
order by d.the_date;
The issue is that when I specify in the condition t.account=111 I don't get the dates on which account 111 did NOT make any transactions.
Only if I remove from the condition t.account=111 I DO get the dates with no transactions (i.e. the LEFT OUTER JOIN works). Why does this happen?
Conditions on the second table need to go into the on clause:
select d.the_date, t.account, t.amount
from dates d left join
transactions t
on t.tx_date = d.the_date and t.account = 111
where d.the_date >= '2016-01-02' and d.the_date <= '2017-12-30'
order by d.the_date;
Otherwise, the value of t.account is NULL in the where clause, turning the outer join into an inner join.

SQL Create dates between date range for multiple dimensions

I have two tables:
SELECT DISTINCT ServLine,RoomID,StartDt,EndDt
INTO #raw
FROM table
SELECT CALENDAR_DATE
INTO #cal
FROM caldendar
How would I write a query to show each CALENDAR_DATE from the #cal table that is in between each StartDt and EndDt for each RoomID and ServLine from the #raw table.
Thank you
With a join. Depending on exactly what input and desired output you have, the join may vary, but an inner join seems like a good place to start without more information.
select *
from #raw r
inner join #cal c
on c.date >= r.startdt
and c.date <= r.enddt

SQL on joining tables (Month and subtextures that has no sales)

DBMS: Derby Embedded
Hello I wonder how I can make some outcome like
SubTextureID Year Month NetSales
1 2013 10 1000
2 2013 10 2000
3 2013 10 0
The third row never appears if that product
has no sales(no records) in the order detail table
Any help would be greatly appreciated!
Thanks
Jack
select s.TextureName, s.SubTextureId, sum(COALESCE(d.NetSales, 0)) NetSales
from (select SubTextureId, TextureName from subtexture) as s
join
(select SubTextureId, ProductCode from products) as p
on (p.SubTextureId = s.SubTextureId)
left outer join
(select ProductCode, OrderCode, NetSales from order_details) as d
on (d.ProductCode = p.ProductCode)
left outer join
( select YEAR(o.PurchaseDateTime) y,
MONTH(o.PurchaseDateTime) m,
OrderCode
from orders o
where o.PurchaseDateTime between '2013-11-01 00:00:00' and '2013-11-30 23:59:59' -- make use of an index if one exists
) as o
on (o.orderCode = d.orderCode)
group by s.TextureName, s.SubTextureId, o.y, o.m
because you used LEFT OUTER JOIN, try to use RIGHT OUTER JOIN, if you understand what's difference about LEFT and RIGHT OUTER JOIN, you will handle your problem

Cross Join Query

Rewording my original post for further clarification.
I current have the below tables:
Product_Ref
product_id
product_name
Products
product_id
so_date (date)
total_sales
Calendar
dt (date field, each row representing a single day for the past/next 10 years)
I am looking to produce a report that will tell me the number of products that were sold in the past 6 months (based on SYSDATE) on a daily basis, the report should be every combination of day in the last 6 months against every possible product_id in the format:
Product id | date | total sales
If I assume that there were 0 entries in the products table (i.e no sales) I would still expect a complete report output but instead it would show 6 months of zero'd data i.e.
1 | 2012-01-01 | 0
2 | 2012-01-01 | 0
3 | 2012-01-01 | 0
1 | 2012-01-02 | 0
2 | 2012-01-02 | 0
3 | 2012-01-02 | 0
…
This would assume there were 3 products in the product_reference table - my original query (noted below) was my starter for 10, but not sure where to go from here.
SELECT products.product_id, calendar.dt, products.total_sales
FROM products RIGHT JOIN calendar ON (products.so_date = calendar.dt)
WHERE calendar.dt < SYSDATE AND calendar.dt >= ADD_MONTHS(SYSDATE, -7)+1
ORDER BY calendar.dt ASC, products.product_id DESC;
The clue is in the question - you are looking for a CROSS JOIN.
SELECT products.product_id, calendar.dt, products.total_sales
FROM Product_Ref
CROSS JOIN calendar
LEFT JOIN products ON products.so_date = calendar.dt
AND products.product_id = Product_Ref.product_id
WHERE calendar.dt < SYSDATE AND calendar.dt >= ADD_MONTHS(SYSDATE, -7)+1
ORDER BY calendar.dt ASC, products.product_id DESC;
I was confused at first by your table names where "Product" in fact means "sale" and "Product_Ref" is a product!
This is very similar to an example of the use of CROSS JOIN I once posted here.
As far as I understood, what do you want is to have no result if there were no sales, write?
So, I think you just need to change the RIGHT JOIN to INNER JOIN.
By RIGHT joining, if there were register in the JOIN table and there weren't in the FROM table it will return the data from the JOIN table, with NULL values in the columns refering to the FROM table.
By INNER joining you will have results just if you there were data that match in both tables.
Hope I understood well and it helps.
Assuming your desired output is to match only the products date with those in the calendar table, you should use an INNER JOIN:
SELECT c.dt, p.product_id, p.total_sales
FROM calendar c
INNER JOIN products p on c.dt = p.so_date
WHERE c.dt < SYSDATE and c.dt >= ADD_MONTHS(SYSDATE,-7)+1
ORDER BY c.dt ASC, p.product_id DESC;
A CROSS JOIN would produce results with every combination from your products table and your calendar table and thus not require the use of ON.
--EDIT
See edits below (UNTESTED):
SELECT PR.Product_ID, C.dt, P.TotalSales
FROM Calendar C
CROSS JOIN Product_Ref PR
LEFT JOIN Product P ON P.Product_Id = PR.Product_Id and p.so_date = c.dt
WHERE c.dt < SYSDATE and c.dt >= ADD_MONTHS(SYSDATE,-7)+1
ORDER BY c.dt ASC, p.product_id DESC;
Good luck.