SQL not delivering expected result with RIGHT JOIN - sql

I have been working on this query for some time now, and reading right join question after right join question here on SO, but I cannot figure this one out.
I have the following Query:
DECLARE #ExpectedDateSample VARCHAR(10)
DECLARE #Date datetime
DECLARE #DaysInMonth INT
DECLARE #i INT
--GIVE VALUES
SET #ExpectedDateSample = SUBSTRING(CONVERT(VARCHAR, DATEADD(MONTH, +0, GETDATE()), 112),5,2)+'/'+CONVERT(VARCHAR(4), DATEPART(YEAR, GETDATE()))
SET #Date = Getdate()
SELECT #DaysInMonth = datepart(dd,dateadd(dd,-1,dateadd(mm,1,cast(cast(year(#Date) as varchar)+'-'+cast(month(#Date) as varchar)+'-01' as datetime))))
SET #i = 1
--MAKE TEMP TABLE
CREATE TABLE #TempDays
(
[days] VARCHAR(50)
)
WHILE #i <= #DaysInMonth
BEGIN
INSERT INTO #TempDays
VALUES(#i)
SET #i = #i + 1
END
SELECT DATEPART(DD, CONVERT(DATE, a.budg_tempDODate1, 103)) ExpectedDate, SUM(a.budg_do1_total) ExpectedAmount
FROM CRM.dbo.budget a
RIGHT JOIN #TempDays on DATEPART(DD, CONVERT(DATE, a.budg_tempDODate1, 103)) = #TempDays.days
WHERE DATEPART(MONTH, a.budg_tempDODate1) = DATEPART(MONTH, GetDate()) AND DATEPART(YEAR, a.budg_tempDODate1) = DATEPART(YEAR, GetDate())
GROUP BY a.budg_tempDODate1
--DROP TABLE TO ALLOW CREATION AGAIN
DROP TABLE #TempDays
In my Budget table I have a few days out of the month missing, but that is why I create a Temp table to count all the days of the month. And then RIGHT join to that Temp table.
I am trying to calculate how much cash is expected on each day of the month. If the day does not exist in my budget table, DO NOT leave it out completely, but rather display 0 is expected.
What I am currently getting
+------+---------------+
| DAYS | AMOUNT |
+------+---------------+
| 1 | 34627.000000 |
| 2 | 72474.000000 |
| 3 | 27084.000000 |
| 4 | 9268.000000 |
| 5 | 32304.000000 |
| 6 | 23261.000000 |
| 7 | 5614.000000 |
| 9 | 3464.000000 |
| 10 | 20046.000000 |
| 12 | 7449.000000 |
| 13 | 265163.000000 |
| 14 | 24210.000000 |
| 15 | 68848.000000 |
| 16 | 31702.000000 |
| 17 | 2500.000000 |
| 19 | 2914.000000 |
| 20 | 238406.000000 |
| 21 | 15642.000000 |
| 22 | 2514.000000 |
| 23 | 46521.000000 |
| 24 | 34093.000000 |
| 25 | 899081.000000 |
| 26 | 204085.000000 |
| 27 | 316341.000000 |
| 28 | 48826.000000 |
| 29 | 2657.000000 |
| 30 | 440401.000000 |
+------+---------------+
What I was Expecting:
+------+---------------+
| DAYS | AMOUNT |
+------+---------------+
| 1 | 34627.000000 |
| 2 | 72474.000000 |
| 3 | 27084.000000 |
| 4 | 9268.000000 |
| 5 | 32304.000000 |
| 6 | 23261.000000 |
| 7 | 5614.000000 |
| 8 | NULL |
| 9 | 3464.000000 |
| 10 | 20046.000000 |
| 11 | NULL |
| 12 | 7449.000000 |
| 13 | 265163.000000 |
| 14 | 24210.000000 |
| 15 | 68848.000000 |
| 16 | 31702.000000 |
| 17 | 2500.000000 |
| 18 | NULL |
| 19 | 2914.000000 |
| 20 | 238406.000000 |
| 21 | 15642.000000 |
| 22 | 2514.000000 |
| 23 | 46521.000000 |
| 24 | 34093.000000 |
| 25 | 899081.000000 |
| 26 | 204085.000000 |
| 27 | 316341.000000 |
| 28 | 48826.000000 |
| 29 | 2657.000000 |
| 30 | 440401.000000 |
+------+---------------+
As you can see, the expected result still shows the days Im not expecting any value.
Can Anybody notice anything immediately wrong with my query... Any help and tips would be greatly appreciated.
I'm using SQL server 2008
Thanks!
Mike

Change your where clause to part of the join, and display the day value from #tempdays, not the data table
SELECT
#TempDays.days ExpectedDate, SUM(a.budg_do1_total) ExpectedAmount
FROM CRM.dbo.budget a
RIGHT JOIN #TempDays on
DATEPART(DD, CONVERT(DATE, a.budg_tempDODate1, 103)) = #TempDays.days
AND DATEPART(MONTH, a.budg_tempDODate1) = DATEPART(MONTH, GetDate())
AND DATEPART(YEAR, a.budg_tempDODate1) = DATEPART(YEAR, GetDate())
GROUP BY #TempDays.days

The problem is your Where Clause -
WHERE DATEPART(MONTH, a.budg_tempDODate1) = DATEPART(MONTH, GetDate()) AND DATEPART(YEAR, a.budg_tempDODate1) = DATEPART(YEAR, GetDate())

The where clause and the group by clauses are applied on the table with less data.
So, the result set is getting confined to the table with less data ignoring the join clauses.
I have rewritten this and tested it.
declare #budget table
(
budg_tempDODate1 datetime,
budg_do1_total float
)
insert into #budget(budg_tempDODate1, budg_do1_total)
select '2014-06-01', 25
union select '2014-06-01', 23
union select '2014-06-02', 23
union select '2014-06-02', 23
union select '2014-06-02', 23
union select '2014-06-03', 23
union select '2014-06-04', 23
union select '2014-06-05', 23
union select '2014-06-05', 23
union select '2014-06-05', 23
union select '2014-06-06', 23
union select '2014-06-07', 23
union select '2014-06-08', 23
union select '2014-06-09', 23
union select '2014-06-10', 23
DECLARE #ExpectedDateSample VARCHAR(10)
DECLARE #Date datetime
DECLARE #DaysInMonth INT
DECLARE #i INT
--GIVE VALUES
SET #ExpectedDateSample = SUBSTRING(CONVERT(VARCHAR, DATEADD(MONTH, +0, GETDATE()), 112),5,2)+'/'+CONVERT(VARCHAR(4), DATEPART(YEAR, GETDATE()))
SET #Date = Getdate()
SELECT #DaysInMonth = datepart(dd,dateadd(dd,-1,dateadd(mm,1,cast(cast(year(#Date) as varchar)+'-'+cast(month(#Date) as varchar)+'-01' as datetime))))
SET #i = 1
--MAKE TEMP TABLE
CREATE TABLE #TempDays
(
[days] int
)
WHILE #i <= #DaysInMonth
BEGIN
INSERT INTO #TempDays
VALUES(#i)
SET #i = #i + 1
END
--select * from #TempDays
SELECT
td.days as ExpectedDate,
SUM(isnull(a.budg_do1_total, 0)) ExpectedAmount
FROM
--CRM.dbo.budget a
#TempDays td
left outer JOIN #budget a on
DATEPART(MONTH, a.budg_tempDODate1) = DATEPART(MONTH, GetDate())
AND DATEPART(YEAR, a.budg_tempDODate1) = DATEPART(YEAR, GetDate())
AND DATEPART(DD, CONVERT(DATE, a.budg_tempDODate1, 103)) = td.days
GROUP BY
--a.budg_tempDODate1
td.days
--DROP TABLE TO ALLOW CREATION AGAIN
DROP TABLE #TempDays
Here is the sql fiddle

Related

Get count for each time row appears in the range between 2 dates

I'am trying to calculate how many times a row "appears" a in the range between 2 dates and grouping them by the month.
So, let's say i have rows that look like this:
Name | StartDate | EndDate
-----------|-----------------|------------
Mathias | 2017-01-01 | 2017-04-01
Lucas | 2017-01-01 | 2017-04-01
i would like to get the output that shows how many records exists between the 2 dates in a query, so something like the following output:
Count | Year | Month
-----------|-----------------|------------
2 | 2017 | 1
2 | 2017 | 2
2 | 2017 | 3
2 | 2017 | 4
0 | 2017 | 5
0 | 2017 | 6
what i've tried is:
SELECT COUNT(*) as COUNT, YEAR(StartDate) YEAR, MONTH(StartDate) MONTH
FROM NamesTable
WHERE Start >= '2017-01-01 00:00:00'
AND Slut <= '2017-06-01 00:00:00'
group by YEAR(StartDate), MONTH(StartDate)
where this is giving me the expected output of:
Count | Year | Month
-----------|-----------------|------------
2 | 2017 | 1
0 | 2017 | 2
0 | 2017 | 3
0 | 2017 | 4
0 | 2017 | 5
0 | 2017 | 6
Because of grouping by the "start date", how can i count rows in the month for every one it expands across?
You need a table with the months range
Table allMonths
+---------+------------+------------+
| monthId | StartDate | EndDate |
+---------+------------+------------+
| 1 | 2017-01-01 | 2017-01-02 |
| 2 | 2017-01-02 | 2017-01-03 |
| 3 | 2017-01-03 | 2017-01-04 |
| 4 | 2017-01-04 | 2017-01-05 |
| 5 | 2017-01-05 | 2017-01-06 |
| 6 | 2017-01-06 | 2017-01-07 |
| 7 | 2017-01-07 | 2017-01-08 |
| 8 | 2017-01-08 | 2017-01-09 |
| 9 | 2017-01-09 | 2017-01-10 |
| 10 | 2017-01-10 | 2017-01-11 |
| 11 | 2017-01-11 | 2017-01-12 |
| 12 | 2017-01-12 | 2018-01-01 |
+---------+------------+------------+
Then your query is:
SELECT am.startDate, COUNT(y.Name)
FROM allMonths am
LEFT JOIN yourTable y
ON am.StartDate <= y.EndDate
AND am.EndDate >= y.StartDate
GROUP BY am.startDate
NOTE: You need to check border cases. Maybe you need change >= to > or change EndDate to the last day of the month.
So, what i ended up doing was something like Juan Carlos proposed, but instead of creating a table i made it up with CTE instead for a cleaner approach:
Declare #todate datetime, #fromdate datetime, #firstOfMonth datetime, #lastOfMonth datetime
Select
#fromdate='2017-01-11',
#todate='2017-12-21',
#firstOfMonth = DATEADD(month, DATEDIFF(month, 0, #fromdate), 0), ----YEAR(#fromdate) + MONTH(#fromdate) + DAY(1),
#lastOfMonth = DATEADD(month, ((YEAR(#fromdate) - 1900) * 12) + MONTH(#fromdate), -1)
;with MonthTable (MonthId, StartOfMonth, EndOfMonth) as
(
SELECT MONTH(#firstOfMonth) as MonthId, #firstOfMonth as StartOfMonth, #lastOfMonth as EndOfMonth
UNION ALL
SELECT MONTH(DATEADD(MONTH, 1, StartOfMonth)), DATEADD(MONTH, 1, StartOfMonth), DATEADD(MONTH, 1, EndOfMonth)
FROM MonthTable
WHERE StartOfMonth <= #todate
)
SELECT am.StartOfMonth, COUNT(y.Start) as count
FROM MonthTable am
left JOIN clientList y
ON y.Start <= am.StartOfMonth
AND y.End >= am.EndOfMonth
GROUP BY am.StartOfMonth

Get list of counts by date

I have two tables. One containing a list of applications. And another one containing counts associated to them every week. Now I want to get as a result the app name and the count for this week and the previous. Let me explain this.
app:
+----+-------------+
| id | name |
+----+-------------+
| 1 | Office 2007 |
+----+-------------+
| 2 | Office 2010 |
+----+-------------+
| 3 | Office 2013 |
+----+-------------+
count:
+----+--------+-------+------------+
| id | app_id | count | date |
+----+--------+-------+------------+
| 1 | 1 | 200 | 2016-01-11 |
+----+--------+-------+------------+
| 2 | 2 | 500 | 2016-01-11 |
+----+--------+-------+------------+
| 3 | 3 | 750 | 2016-01-11 |
+----+--------+-------+------------+
| 4 | 1 | 180 | 2016-01-18 |
+----+--------+-------+------------+
| 5 | 2 | 378 | 2016-01-18 |
+----+--------+-------+------------+
| 6 | 3 | 1000 | 2016-01-18 |
+----+--------+-------+------------+
And this is the result I need. I need all the applications with the count of this week and the previous:
+-------------+-----------------+-----------------+
| app | count_this_week | count_prev_week |
+-------------+-----------------+-----------------+
| Office 2007 | 180 | 200 |
+-------------+-----------------+-----------------+
| Office 2010 | 378 | 500 |
+-------------+-----------------+-----------------+
| Office 2013 | 1000 | 750 |
+-------------+-----------------+-----------------+
A script runs every week which fills the count table. And now I need to get a report also on a weekly basis.
Honestly I'm a bit lost as I don't know how to declare the conditions for the columns.
You can try to group first by DATEPART(WEEK,C.date),name and then split the counts into 2 columns using another GROUP BY. Something like this
EDIT
If there are exactly 1 record per week per app, you can do with just one group by like this.
SELECT
appname,
SUM(CASE WHEN weekno = 0 THEN sumcount ELSE 0 END) as thisweek,
SUM(CASE WHEN weekno = 1 THEN sumcount ELSE 0 END) as lastweek
FROM
(
SELECT
DATEPART(WEEK,CURRENT_TIMESTAMP) - DATEPART(WEEK,C.date) as weekno,
name as appname,
count as sumcount
FROM App A
INNER JOIN CountTable C ON A.[id] = C.[app_id]
WHERE DATEPART(WEEK,C.date) BETWEEN DATEPART(WEEK,CURRENT_TIMESTAMP) - 1 AND DATEPART(WEEK,CURRENT_TIMESTAMP)
)T
GROUP BY appname
Query
SELECT
appname,
SUM(CASE WHEN weekno = 0 THEN sumcount ELSE 0 END) as thisweek,
SUM(CASE WHEN weekno = 1 THEN sumcount ELSE 0 END) as lastweek
FROM
(
SELECT
DATEPART(WEEK,CURRENT_TIMESTAMP) - DATEPART(WEEK,C.date) as weekno,
name as appname,
SUM(count) as sumcount
FROM App A INNER JOIN CountTable C ON A.[id] = C.[app_id]
WHERE DATEPART(WEEK,C.date) BETWEEN DATEPART(WEEK,CURRENT_TIMESTAMP) - 1 AND DATEPART(WEEK,CURRENT_TIMESTAMP)
GROUP BY DATEPART(WEEK,C.date),name
) AS T
GROUP BY appname
SQL Fiddle
Output
| appname | thisweek | lastweek |
|-------------|----------|----------|
| Office 2007 | 180 | 200 |
| Office 2010 | 378 | 500 |
| Office 2013 | 1000 | 750 |
You can use this generic query with a variable for the current week day:
DECLARE #week date = '2016-01-18';
WITH data AS (
SELECT a.name, c.[count]
, w = CASE WHEN c.[date] = #week THEN 0 ELSE 1 END
FROM #Counts c
INNER JOIN #Apps a ON c.app_id = a.id
WHERE [date] = #week OR [date] = DATEADD(day, -7, #week)
)
SELECT App = name, count_this_week = [0], count_prev_week = [1]
FROM data d
PIVOT (
MAX([count])
FOR w IN ([0], [1])
) p
Output:
App count_this_week count_prev_week
Office 2007 180 200
Office 2010 378 500
Office 2013 1000 750
Your data:
DECLARE #Apps TABLE ([id] int, [name] varchar(11));
DECLARE #Counts TABLE([id] int, [app_id] int, [count] int, [date] date);
INSERT INTO #Apps([id], [name])
VALUES
(1, 'Office 2007'),
(2, 'Office 2010'),
(3, 'Office 2013')
;
INSERT INTO #Counts([id], [app_id], [count], [date])
VALUES
(1, 1, 200, '2016-01-11'),
(2, 2, 500, '2016-01-11'),
(3, 3, 750, '2016-01-11'),
(4, 1, 180, '2016-01-18'),
(5, 2, 378, '2016-01-18'),
(6, 3, 1000, '2016-01-18')
;
SELECT *
FROM count
JOIN app ON app.id=count.app_id
WHERE date BETWEEN '2016-01-18' AND '2016-01-11'

SQL Days before end of the month

i have got table with transactions, looking like:
+----+--------------+----------------+------+
| ID | OrderDate | DeliveryDate | EUR |
+----+--------------+----------------+------+
| 1 | 2015-02-21 | 2015-02-25 | 100 |
| 2 | 2015-03-01 | 2015-03-14 | 110 |
| 3 | 2015-03-01 | 2015-03-17 | 90 |
| 4 | 2015-03-10 | 2015-03-20 | 250 |
| 5 | 2015-03-31 | 2015-03-31 | 350 |
+----+--------------+----------------+------+
ANd I need to get sum of revenue and number of orders (COUNT of IDs) based on Days before the end of the month when order gets delivered.
SELECT datediff(day, OrderDate, CAST(DATEADD(month, DATEDIFF(month,0,getdate()+1,0)-1) as Date) as DBEOM, SUM(EUR) as Rev, COUNT(ID) as NumberOfOrders
FROM transactions
WHERE MONTH(DeliveryDate) = 3 AND YEAR(DeliveryDate) = 2015
GROUP BY datediff(day, OrderDate, CAST(DATEADD(month, DATEDIFF(month,0,getdate()+1,0)-1) as Date) as DBEOM
ORDER BY 1
The result in this case would be like:
+-----+-----+----------------+
|DBEOM| Rev | NumberOfOrders |
+-----+-----+----------------+
| 0 | 350 | 1 |
| 21 | 250 | 1 |
| 30 | 200 | 2 |
+-----+-----+----------------+
This is done in SQL 2008, so I can't simply use EOMONTH. I have tried, what is above, but i am getting
ERROR -
[Microsoft][ODBC SQL Server Driver][SQL Server]The datediff function
requires 3 argument(s).
Many thanks in advance for advice!
The easiest way I've found get the last day of the month with more primitive functions is to get the first day of the next month and then subtract a day.
I'm not a TSQL guy so this syntax likely won't be correct but you need something more like
DATEADD(day, DATEFROMPARTS(DATEPART(year, DATEADD(month,1,getdate()), DATEPART(month, DATEADD(month,1,getdate()), 1), -1)
Try:
SELECT datediff(day,
OrderDate,
dateadd(DAY,
-1,
dateadd(MONTH,
1,
dateadd(DAY,
1-day(DeliveryDate),
DeliveryDate
)
)
)
) as DBEOM, SUM(EUR) as Rev, COUNT(ID) as NumberOfOrders
FROM t
WHERE MONTH(DeliveryDate) = 3 AND YEAR(DeliveryDate) = 2015
GROUP BY datediff(day,
OrderDate,
dateadd(DAY,
-1,
dateadd(MONTH,
1,
dateadd(DAY,
1-day(DeliveryDate),
DeliveryDate
)
)
)
)
ORDER BY 1
sqlfiddle.com

updates in month and day differences

I have a table as shown below:
Note: the MAX last_orderdate is 20131015 and the format is yyyymmdd.
I would like to show the final result looks like below:
Is there any query to help me in this as I have 200000 plus records.
Thank you very much for spending your time to look at my question.
For DATEDIFF() function
Try this:
UPDATE A
SET A.monthDiff = DATEDIFF(mm, CONVERT(DATE, A.orderDate, 112), B.lastOrderDate),
A.dayDiff = DATEDIFF(dd, CONVERT(DATE, A.orderDate, 112), B.lastOrderDate)
FROM tableA A, (SELECT MAX(CONVERT(DATE, orderDate, 112)) lastOrderDate FROM tableA) B
Check the SQL FIDDLE DEMO
OUTPUT
| ID | ORDERDATE | MONTHDIFF | DAYDIFF |
|----|-----------|-----------|---------|
| 1 | 20130105 | 9 | 283 |
| 2 | 20130205 | 8 | 252 |
| 3 | 20130305 | 7 | 224 |
| 4 | 20130909 | 1 | 36 |
| 5 | 20131001 | 0 | 14 |
| 6 | 20131015 | 0 | 0 |
try something like this:
declare #a date
set #a='20130105'
declare #b date
set #b='20131015'
select datediff(d,#a,#b) as date_diff,datediff(m,#a,#b) as month_diff
Try this.
select DATEDIFF(DAYOFYEAR,'20131015','20131125').
DAYOFYEAR represents count of days. Depeneds on your requirement, you can change to see month,day or year difference using DATEDIFF

converting varchar(50) to datetime and populating rest of the table

I am working on a data warehouse and in the data model theres a dimension table dim.date which needs to be populated from excel sheet.
this is table structure
CREATE TABLE timeme
(
datekey INT IDENTITY NOT NULL,
fulldate VARCHAR(50),
englishdayname VARCHAR(20),
englishmonthname VARCHAR(20),
daynumberofthemonth INT,
daynumbarofyear INT,
weeknumberofyear INT,
calenderquater INT,
calenderyear INT
)
INSERT INTO timeme
(fulldate)
VALUES ('03-13-2013-02:41:38'),
('03-13-2013-02:39:50'),
('03-13-2013-02:53:15'),
('03-13-2013-02:59:47')
This is what i tried
-- select * from timeme
--update timeme set fulldate= cast(left(fulldate, 10)+ ' ' +replace(substring(fulldate, 12, 12), '.', ':') as datetime)-- but it should be 2013-03-13 02:41:38.000
UPDATE timeme
SET englishdayname = Datename(dw, fulldate),
englishmonthname = Datename(mm, fulldate),
daynumberofthemonth = Datepart(d, fulldate)
Update timeme
SET daynumbarofyear -- this i didnt get rest how to populate
can somebody help me populating entire table
Are you looking for this?
UPDATE timeme
SET fulldate = SUBSTRING(fulldate, 7, 4) + '-' + LEFT(fulldate, 5) + ' ' + RIGHT(fulldate, 8);
UPDATE timeme
SET englishdayname = DATENAME(dw, CAST(fulldate AS DATE)),
englishmonthname = DATENAME(mm, CAST(fulldate AS DATE)),
daynumberofthemonth = DATEPART(dd, CAST(fulldate AS DATE)),
daynumbarofyear = DATEPART(dy, CAST(fulldate AS DATE)),
weeknumberofyear = DATEPART(ww, CAST(fulldate AS DATE)), -- or isoww instead of ww
calenderquater = DATEPART(qq, CAST(fulldate AS DATE)),
calenderyear = DATEPART(yy, CAST(fulldate AS DATE));
Outcome:
| DATEKEY | FULLDATE | ENGLISHDAYNAME | ENGLISHMONTHNAME | DAYNUMBEROFTHEMONTH | DAYNUMBAROFYEAR | WEEKNUMBEROFYEAR | CALENDERQUATER | CALENDERYEAR |
|---------|---------------------|----------------|------------------|---------------------|-----------------|------------------|----------------|--------------|
| 1 | 2013-03-13 02:41:38 | Wednesday | March | 13 | 72 | 11 | 1 | 2013 |
| 2 | 2013-03-13 02:39:50 | Wednesday | March | 13 | 72 | 11 | 1 | 2013 |
| 3 | 2013-03-13 02:53:15 | Wednesday | March | 13 | 72 | 11 | 1 | 2013 |
| 4 | 2013-03-13 02:59:47 | Wednesday | March | 13 | 72 | 11 | 1 | 2013 |
Here is SQLFiddle demo