SQL Server full outer group not getting all values - sql

I am trying to full join two tables by their date with one table having columns call 'date', 'parties', 'total', etc and another table just having dates.
Below is the query I have:
SELECT
rangeDates.[ListOFDates],
partiesDetails.[Party], partiesDetails.[Amount]
FROM rangeDates
FULL OUTER JOIN partiesDetails
ON rangeDates.[ListOFDates] = partiesDetails.[Date]
Now is the table rangeDates I have. Also there's a date for everyday for a set period of dates, for exmaple, below it starts at '2017-02-02' and may have a date everyday till '2018-03-01'
Ref ListOFDates
1 2017-02-02
2 2017-02-03
3 2017-02-04
.........
And in the partiesDetails table
Date Party Amount
2017-02-03 Tuf 5000
2017-04-01 Tuf 2000
2017-05-22 Wing 3000
.................
The ideal results I would want is:
ListOfDates Party Amount
2017-02-02 NULL NULL
2017-02-03 Tuf 5000
2017-02-04 NULL NULL
............

I feel that maybe you should be using a calendar table here:
WITH dates AS (
SELECT CAST('20170202' AS date) AS [date]
UNION ALL
SELECT DATEADD(dd, 1, [date])
FROM dates
WHERE DATEADD(dd, 1, [date]) <= '2018-03-01'
)
SELECT
d.date,
p.[Party],
p.[Amount]
FROM dates d
LEFT JOIN partiesDetails p
ON d.date = p.[Date]
ORDER BY
d.date;
I make this suggestion because you used the language and may have a date everyday till, which seems to imply that maybe the rangeDates table does not in fact cover the entire date range you have in mind.

Related

SQL reflect data for all days

I am trying to create a table in SQL where I reflect data for all days of a particular month.
For example, even if there is no Sale transaction for a particular day, the day for the particular employee should still be reflective in the table.
Scenario for the question. I am working with the following dates for this example: 1st, 2nd, 3rd and 4th Jan 2022.
The raw data is as follows:
Name
Date
SaleAmount
John
2022-01-01
154875
John
2022-01-03
598752
As seen above, we only have data for the 1st and 3rd.
The outcome should look as follows:
Name
Date
SaleAmount
John
2022-01-01
154875
John
2022-01-02
NULL
John
2022-01-03
598752
John
2022-01-04
NULL
As seen above, the 2nd and 4th should be included even though there was no activity for those days.
What I am currently trying:
I have a master date table which is being used as a RIGHT JOIN on the transaction table. However, the final outcome of my table is as follows:
Name
Date
SaleAmount
John
2022-01-01
154875
NULL
2022-01-02
NULL
John
2022-01-03
598752
NULL
2022-01-04
NULL
As seen above, the 'Name' field returns as NULL. The SaleAmount however should reflect NULL to indicate no transactions happening.
I would appreciate any assistance on this.
Seems like you want to
Start with the date table
Cross join to your employee/salesperson table so you now have one row for each salesperson on each date
Left join the sales orders for that date + salesperson combo to get the sum of their sales for that day. If they have none, it'll show null:
select emp.Name
,dat.Date
,sum(ord.Amount) as SaleAmount
from dateList dat
cross join salesPerson emp
left join salesOrder ord on ord.OrderDate = dat.Date and ord.SalesPersonId = emp.SalesPersonId
group by emp.Name
,dat.Date
you can create a list of dates on the fly. as example per month
Declare #year int = 2022, #month int = 7;
WITH numbers
as
(
Select 1 as value
UNion ALL
Select value + 1 from numbers
where value + 1 <= Day(EOMONTH(datefromparts(#year,#month,1)))
)
SELECT datefromparts(#year,#month,numbers.value) Datum FROM numbers
then left join to your table.
You may use a Recursive CTE as the following:
Declare #startDate as date ='2022-01-01';
Declare #endDate as date ='2022-01-31';
With CTE As
(
Select Distinct Name_ nm,#startDate dt From SalesDates
Where Date_ Between #startDate And #endDate
Union All
Select nm, DateAdd(Day,1,dt) From CTE
Where DateAdd(Day,1,dt)<=#endDate
)
Select C.nm as [Name],C.dt as [Date], S.SaleAmount
From CTE C Left Join SalesDates S
On S.Date_=C.dt
And S.Name_=C.nm
Order By C.nm,C.dt
You can change the values of #startDate and #endDate according to the period you want.
See a demo from db<>fiddle.

T-sql count number of times a week on rows with date interval

If you have table like this:
Name
Data type
UserID
INT
StartDate
DATETIME
EndDate
DATETIME
With data like this:
UserID
StartDate
EndDate
21
2021-01-02 00:00:00
2021-01-02 23:59:59
21
2021-01-03 00:00:00
2021-01-04 15:42:00
24
2021-01-02 00:00:00
2021-01-06 23:59:59
And you want to calculate number of users that is represented on each day in a week with a result like this:
Year
Week
NumberOfTimes
2021
1
8
2021
2
10
2021
3
4
Basically I want to to a Select like this:
SELECT YEAR(dateColumn) AS yearname, WEEK(dateColumn)as week name, COUNT(somecolumen)
GROUP BY YEAR(dateColumn) WEEK(dateColumn)
The problem I have is the start and end date if the date goes over several days I want it to counted each day. Preferably I don't want the same user counted twice each day. There are millions of rows that are constantly being deleted and added so speed is key.
The database is MS-SQL 2019
I would suggest a recursive CTE:
with cte as (
select userid, startdate, enddate
from t
union all
select userid, startdate,
enddate
from cte
where startdate < enddate and
week(startdate) <> week(enddate)
)
select year(startdate), week(startdate), count(*)
from cte
group by year(startdate), week(startdate)
option (maxrecursion 0);
The CTE expands the data by adding 7 days to each row. This should be one day per week.
There is a little logic in the second part to handle the situation where the enddate ends in the same week as the last start date. The above solution assumes that the dates are all in the same year -- which seems quite reasonable given the sample data. There are other ways to prevent this problem.
You need to cross-join each row with the relevant dates.
Create a calendar table with columns of years and weeks, include a start and end date of the week. See here for an example of how to create one, and make sure you index those columns.
Then you can cross-join like this
SELECT
YEAR(dateColumn) AS yearname,
WEEK(dateColumn)as weekname,
COUNT(somecolumen)
FROM Table t
JOIN CalendarWeek c ON c.StartDate >= t.StartDate AND c.EndDate <= t.EndDate
GROUP BY YEAR(dateColumn), WEEK(dateColumn)

How can I pivot my T-SQL query output to get this specific table?

I am running a T-SQL query on SQL Server 2014. The query and its output are given below:
Use MyDatabase
SELECT
ID,
ArrivalMonth,
DateOfBirth
FROM [View1]
WHERE [ArrivalMonth] between '2017-01-01' and '2018-05-01'
The output of the above query looks like this (extract):
ID ArrivalMonth DateOfBirth
101 2017-01-01 1974-05-30
105 2017-05-01 1967-03-05
125 2017-05-01 NULL
... ... ...
I need a T-SQL query to give me the following output (based on the output above):
ArrivalMonth Number_Of_Bookings Number_Of_DOB_Captured
2017-01-01 130 110
2017-02-01 90 85
... ... ...
2018-05-01 115 70
The first column is the ArrivalMonth. Number_Of_Bookings is the count of number of records from the above query. Number_Of_DOB_Captured is the count of DateOfBirth which is NOT NULL.
I think may be the Pivot query might be the solution but I am confused as to how to execute it in this scenario.
You may left join a calendar table containing all the months to your current table, and then aggregate:
WITH months AS (
SELECT '2017-01-01' AS month UNION ALL
SELECT '2017-02-01' UNION ALL
...
SELECT '2017-12-01'
)
SELECT
m.month,
COUNT(*) AS Number_Of_Bookings,
COUNT(v.DateOfBirth) AS Number_Of_DOB_Captured
FROM months m
LEFT JOIN [View1] v
ON m.month = v.ArrivalMonth
WHERE
v.ArrivalMonth BETWEEN '2017-01-01' AND '2018-05-01'
GROUP BY
m.month;
The calendar table may be necessary here if it could be possible that, for some reason, a given arrival month have no data associated with it in your view. If you are certain that the view would always contain data for every month, then you may aggregate directly on your table without joining.
you can use count(Number_Of_Bookings), count(DateOfBirth) and group by ArrivalMonth
So you count the number of non null values for each different ArrivalMonth.
the query :
Select ArrivalMonth
, count(Number_Of_Bookings)
, count(DateOfBirth)
FROM [View1]
WHERE [ArrivalMonth] between '2017-01-01' and '2018-05-01'
group by ArrivalMonth

SQL Query to list records for last 7 days

I need query to list all the records for the date, incase if records are not present then query should list 0
SELECT Count(C.ConversionStatusID) Visits, CONVERT(VARCHAR(10),ActionDate,110) ActionDate
FROM Conversion C
WHERE C.ConversionStatusID = 2 AND
ActionDate Between DateAdd(day,-7,GetDate()) AND GETDATE()
GROUP BY CONVERT(VARCHAR(10),ActionDate,110)
Order BY CONVERT(VARCHAR(10),ActionDate,110) DESC
Expected output should always result 7 records, Sample result should be as below
ActionDate Visits
01-09-2015 1
01-08-2015 5
01-07-2015 0
01-06-2015 0
01-05-2015 3
01-04-2015 8
01-03-2015 0
Thanks in advance
you need to have date table for the last 7 days, you can then do left join
with cte (value, n)
as
(
select DATEADD(DAY, DATEDIFF(day,0,getdate()),0) as value,1 as n
UNION ALL
SELECT DATEADD(day, -1, value) as value, n+1
from cte
where n < 7
)
select CONVERT(VARCHAR(10),cte.value,110) as ActionDate , Count(C.ConversionStatusID) Visits
from cte
left join Conversion C
ON CONVERT(VARCHAR(10),C.ActionDate,110) = CONVERT(VARCHAR(10),cte.value,110)
and C.ConversionStatusID = 2
GROUP BY CONVERT(VARCHAR(10),cte.value,110)
Order BY CONVERT(VARCHAR(10),cte.value,110) DESC
Something like this will work, but you're going to have to play around with what should be a date and what should be a datetime. It's not at all clear where or when you want a time component. In particular, your WHERE clause seems to contradict the rest of your query since it doesn't strip the time. For example, if GETDATE() is January 12 at 2 PM, is January 11 at 1 PM one day ago or two? What about January 5 at 1 PM, then, because the WHERE clause is stripping that off.
If a date is 00:00 to 23:59 (although the WHERE clause is still off):
;WITH Dates ([Date]) AS (
SELECT CAST(DATEADD(DAY,-1,GETDATE()) AS DATE) UNION
SELECT CAST(DATEADD(DAY,-2,GETDATE()) AS DATE) UNION
SELECT CAST(DATEADD(DAY,-3,GETDATE()) AS DATE) UNION
SELECT CAST(DATEADD(DAY,-4,GETDATE()) AS DATE) UNION
SELECT CAST(DATEADD(DAY,-5,GETDATE()) AS DATE) UNION
SELECT CAST(DATEADD(DAY,-6,GETDATE()) AS DATE) UNION
SELECT CAST(DATEADD(DAY,-7,GETDATE()) AS DATE))
SELECT D.Date,
COALESCE(Count(C.ConversionStatusID),0) Visits
FROM Dates D
LEFT JOIN Conversion C
ON D.[Date] = CAST(C.ActionDate AS Date)
WHERE C.ConversionStatusID = 2 AND
ActionDate Between DateAdd(day,-7,GetDate()) AND GETDATE()
GROUP BY CONVERT(VARCHAR(10),ActionDate,110)
Order BY CONVERT(VARCHAR(10),ActionDate,110) DESC
You could get away with not using a CTE if you had a Numbers or tally table, but for something this small it really won't matter that much.

SQL Server Date Selection in Recordset based on initial date and number of days in another recordset

What I'm about to ask about, I had difficulties what I was looking for throughout the forum postings on here, so any assistance at all will be greatly appreciated! :)
To help explain what I want, here's a little snippet of a view result-set that I'm working with:
CalendarDate: WorkDay:
2014-10-03 00:00:00.000 1
2014-10-02 00:00:00.000 1
2014-10-01 00:00:00.000 1
2014-09-30 00:00:00.000 1
2014-09-29 00:00:00.000 1
2014-09-26 00:00:00.000 1
2014-09-25 00:00:00.000 1
This view represents a table in our database that keeps track of actual working days for our company; this view excludes any non-working days (hence all the "1"s).
What I'm trying to do is take a datetime value from another result-set, find it in this result-set and count down the number of days (based on a value being brought in from another result-set as well). So, if I was starting with October 3, 2014 and the number of days I was going back was 5, I want to end up on September 26, 2014.
Personally, I see this being accomplished in a unique record count on a pre-sorted view, but SQL is a diverse universe of ways to do the same thing and I would like to achieve this in the most efficient way possible :).
Like I said at the beginning, I didn't know this question should be properly worded so if this is a duplicate post then I apologize.
you can use row_number analytic function and then derive the difference in days
Assuming your second result set is like this
create table Table2
( StartDate datetime,
days int
);
insert into Table2 values ('2014-10-03', 5);
insert into Table2 values ('2014-10-02', 5);
You can join with current table with this result set and get the required out dates using cte and row_number and self join.
with cte
as
(
select CalendarDate, row_number() over ( order by CalendarDate desc) as rn, WorkDay
from Table1
)
select T1.StartDate, T1.days, T2.CalendarDate as OutDate from
cte
join Table2 T1
on cte.calendarDate = T1.StartDate
join cte T2
on T2.rn - cte.rn = T1.days
result will come out like
STARTDATE DAYS OUTDATE
October, 03 2014 5 September, 26 2014
October, 02 2014 5 September, 25 2014
SQL FIDDLE
And when you use the TOP-Clause:
SELECT TOP 1 CalendarDate
FROM (SELECT TOP 5 CalendarDate
FROM DateTable
WHERE CalendarDate <'2014-10-03'
ORDER BY CalendarDate DESC
) AS T5
ORDER BY CalendarDate ASC