SQL Create dates between date range for multiple dimensions - sql

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

Related

Avoid using CROSS JOIN on my SQL query (too heavy)

I am working on an SQL query in order to define customer types, the goal is to differenciate the old active customers from the churn customers (churn = customers that stopped using your company's product or service during a certain time frame)
In order to do that, i came up with this query that works perfectly :
WITH customers AS (
SELECT
DATE(ord.delivery_date) AS date,
ord.customer_id
FROM table_template AS ord
WHERE cancel_date IS NULL
AND order_type_id IN (1,3)
GROUP BY DATE(ord.delivery_date), ord.customer_id, ord.delivery_date),
days AS (SELECT DISTINCT date FROM customers),
recap AS (
SELECT * FROM (
SELECT
a1.date,
a2.customer_id,
MAX(a2.date) AS last_order,
DATE_DIFF(a1.date, MAX(a2.date), day) AS days_since_last,
MIN(a2.date) AS first_order,
DATE_DIFF(a1.date, MIN(a2.date), day) AS days_since_first
FROM days AS a1
CROSS JOIN customers AS a2 WHERE a2.date <= a1.date
GROUP BY a1.date, customer_id)
)
SELECT * FROM recap
The result of the query :
The only issue of this query is that the calculation is too heavy (it uses a lot of CPU seconds) I think that it is due to the CROSS JOIN.
I need some of your help in order to find another way to come with the same result, a way that doesn't need a CROSS JOIN to have the same output, do you guys think it is possible ?
As you have mentioned the problem of query taking a long time to load was because of the internet issue. Also, I will try to explain Inner Join further with a sample query as below:
SELECT distinct a1.id,a1.date
FROM `table1` AS a1
INNER JOIN `table2` AS a2
ON a2.date <= a1.date
The INNER JOIN selects all rows from both the tables as long as the condition satisfies. In this sample query it gives the result based on condition a2.date <= a1.date only if date values in table1 are greater than or equal to date values in table2.
Input Table 1:
Input Table 2:
Output Table:

DimDate Join to Another Table

I have a DimDate table that I want to join to another table with dates where various visits took place. I have followed many threads on here, but I can't get the missing dates to appear in my result set. Attached is a screenshot of my DimDate table and below is the script. I have two versions with the Dimdate table being the main table and the other version with it as a left join to the other table, and neither bring through the missing dates in my result set. Basically, I am trying to bring through all months and populate with a NULL if there are no entries in my other table.
SELECT
month(d.date) as 'DimDateMonth'
,month(s.date) as 'ActivityMonth'
,year(d.date) as 'DimDateYear'
,[PCN]
,[Type of Visit]
,[Pharmacist]
,[Practice]
,count(distinct(cast(s.Date as date))) 'Number of visits'
FROM [dbo].[DimDate] as d
left join [dbo].[mytable] s on month(d.date) = month(s.date) and year(s.date) = year(d.date)
where s.Pharmacist = 'abc' and year(d.date) = '2020'
group by
month(d.date)
,month(s.date)
,year(d.date)
,[PCN]
,[Type of Visit]
,[Pharmacist]
,[Practice]
Filters on the second table in a LEFT JOIN need to be in the ON clause:
FROM [dbo].[DimDate] d LEFT JOIN
[dbo].[mytable] s
ON month(d.date) = month(s.date) AND
year(s.date) = year(d.date) AND
s.Pharmacist = 'abc'
WHERE year(d.date) = '2020'
Putting the s.Pharmacist = 'abc' in the WHERE clause filters out NULL values -- which undoes the LEFT JOIN.
Note that conditions on the first table should still go in the WHERE clause so the rows really are filtered out.
I would also recommend writing the WHERE clause as:
WHERE d.date >= '2020-01-01' AND
d.date < '2021-01-01'
This allows the query to use and index on (date) for filtering.

How to select the most recent data from SQL joining 3 tables?

I have 3 tables. I need to join the (AGENT table) from (DATA table) by column RANKDATA. There may be multiple entries in the DATA table. I need to select the most recent based on the DATE, then get the CODE_ID and join in CODE table.
Here's my code. I tried to use Max(D.DATE) but I got an error. My only problem, I don't know how to group it by the most recent date.
select A.ID, A.NAME, C.CODE_NAME, D.DATE
from Agent A
JOIN Data D ON A.RANKDATA = D.RANKDATA
JOIN CODE C ON D.CODE_ID = C.CODE_ID
output
I'm not quite clear on what your results are supposed to be from your image, bu t an alternative to doing this with a group by might be to use row_number. row_number basically projects a column with an incrementing integer (1,2,3,...n) over your rows. You can "partition" the function in the same way you would a group by. In this case, I partitioned it by a.RankData, but that was sort of a guess at what you wanted. The order by defines in what order the row numbering will appear. In this case, I've ordered it by d.[date] descending so the most recent date for every a.RankData will be the number 1. Finally, I turn all that into a subquery so I can put the new column in a where clause and just skim off those where RID = 1 (i.e the date is the max date).
select *
from (select
RID = row_number() over(partition by a.RankData order by d.[date] desc),
A.ID,
A.NAME,
C.CODE_NAME,
D.DATE
from Agent A
inner join [Data] D
on A.RANKDATA = D.RANKDATA
inner join CODE C
on D.CODE_ID = C.CODE_ID) a
where a.RID = 1
You could try using a CROSS APPLY, as an additional option.
SELECT A.ID, A.NAME, T.CODE_NAME, T.DATE
FROM Agent A
CROSS APPLY
(SELECT TOP 1 C.CODE_NAME, D.DATE
FROM
Data D JOIN CODE C ON D.CODE_ID = C.CODE_ID
WHERE A.RANKDATA = D.RANKDATA
ORDER BY D.DATE DESC) T
An image helps to visualize. But if you need time from others to help you, it would definitely be useful if you include a simple script to create your test data:
Something like this:
CREATE TABLE Agent (ID INT, NAME VARCHAR(30) ,RANKDATA INT)
INSERT INTO Agent
VALUES (1,'Mark',12), (2,'Joe',13), (3,'Steve',11), (4,'Sam',10)
CREATE TABLE DATA (ID int, RANKDATA int, CODE_ID VARCHAR(2), DATE datetime)
INSERT INTO DATA
VALUES (1,12,'01','20170901 2:30'), (2,13,'02','20170901 6:30'),
(3,11,'03','20170901 4:30'), (4,10,'02','20170901 1:30'),
(5,10,'03','20170901 2:50'), (6,12,'02','20170901 5:30')
CREATE TABLE CODE(ID int, CODE_ID varchar(2), CODE_NAME Varchar(15))
INSERT INTO CODE
VALUES (1,'01','RANK 1'), (2,'02','RANK 2'), (3,'03','RANK 3')

How to include non-matching rows?

This script is working as intended.
select a.Loc, Count(a.PID) as TotalVisit
from AccountCount as a
inner join Data as b
on a.PID = b.PID
where
cast(a.DateTime as date) between cast(b.ADateTime as date) and cast(b.DDateTime as date)
and year(a.DateTime)=2015
and month(a.DateTime)=05
group by a.Loc
order by a.Loc;
However, I need to include few more PID from Data table. These PID is not in AccountCount table.
select LocID, PID
from Data
where
and cast(ADateTime as date) = cast(DDateTime as date)
and year(ADateTime) = 2015
and month(ADateTime)=05
order by LocID;
In simple terms, I need to do union between the first script and the second script. I tried to right join the Data table but it didn't work.
Using the UNION ALL provided by xQbert, I get the result like.
Loc TotalVisit
1st floor 20
2nd floor 5
3rd floor 8
1st floor 2
It needs to be
Loc TotalVisit
1st floor 22
2nd floor 5
3rd floor 8
Please help.
Thank you.
I would think a right join would work so long as the ON criteria is setup correctly and the Where clause is moved to the join (as it makes the right join an inner join again if left in the where clause. (the outer join results in null records which are excluded by the where clause thus negating the outer join))
The union all doesn't allow for the aggregation of data. To me the outer join is the right thing to do here. We just need to understand the data better to make it work correctly. However, using union all you could simply sum up the results... using an outer query... but now that you've given some sample data I might be able to figure out why the outer join wasn't working)
Using union all ... (I'm about getting it working then improving it)
Select X.Loc, sum(X.TotalVisit) as TotalVisit
from (SELECT a.Loc as LOC, Count(a.PID) as TotalVisit
from AccountCount as a
inner join Data as b
on a.PID = b.PID
where
cast(a.DateTime as date) between cast(b.ADateTime as date) and cast(b.DDateTime as date)
group by a.Loc
UNION ALL
select LocID as LOC, count(PID)
from Data
where
and cast(ADateTime as date) = cast(DDateTime as date)
GROUP BY by LocID
) X
GROUP BY X.Loc
ORDER BY X.LOC
This leads me to this... which I think would work Take the first non-null value of location from AccountCount.Loc and Data.LocID and use it. Notice no where clause...
SELECT Coalesce(A.Loc, B.LocID) as Loc, count(B.PID) as TotalVisit
FROM Data B
LEFT JOIN AccountCount A
on B.PID = A.PID
and (cast(a.DateTime as date) between cast(b.ADateTime as date) and cast(b.DDateTime as date)
OR cast(B.ADateTime as date) = cast(B.DDateTime as date))
GROUP BY Coalesce(A.Loc, B.LocID)
Order by Coalesce(A.Loc, B.LocID)

Join query on Date Range

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