How to show default value in group by? - sql-server

I have this query
SELECT DATE,COUNT(DATE) FROM TABLE WHERE DATE BETWEEN DATE1 AND DATE2 GROUP BY DATE..
Result will be like(for date 1/2/2012 to 3/2/2012 i.e 3 dates)
1/2/2012 5
2/2/2012 6
3/2/2012 9
Sometimes if the count is Null means it does not showing date i.e
1/2/2012 5
If (2/2/2012 is not there)
3/2/2012 9
I want to list all dates. i.e like this
1/2/2012 5
2/2/2012 0
3/2/2012 9
How to do that?

You can use a CTE to generate a list of dates, and left join on that:
; with Dates as
(
select cast('2012-01-01' as date) as dt
union all
select dateadd(day, 1, dt)
from Dates
where dateadd(day, 1, dt) < '2012-01-06'
)
select d.dt
, count(yt.id)
from Dates d
left join
YourTable yt
on yt.Date = d.Dt
group by
d.dt;
Live example at SQL Fiddle.

Related

How get last n month of data for each ID from 2 tables using SQL

I have 2 tables, A and B. I want to have last 3 months of Db_date (table B) from the Start_Date (tableA) for each ID.
Table A
ID
Start_Date
1
2022-08-01
Table B
ID
Start_Date
Amount
1
2022-08-01
1000
1
2022-07-01
2000
1
2022-06-01
3000
1
2022-05-01
500
I know by using below code can get last 3 months from the current moth of Start_Date, but I want have last 3 months of Db_Date Amount from table B, for each Start_Date for each ID in Table A.
SELECT *
FROM TableA
WHERE Start_Date >= DATEADD(M, -3, GETDATE())
You could use an outer apply to get the sum of amount the last three months from TableB
SELECT *
FROM TableA a
OUTER APPLY(SELECT SUM(Amount) as SumAmountFor3MonthsAfterStart
FROM TableB
WHERE StartDate > DATEADD(M, -3, a.Start_Date)
) OA
WHERE Start_Date >= DATEADD(M, -3, GETDATE())

Understanding GroupBY

I have a table in SQL Server where personal entries and exits are recorded.
There are multiple hours of entry or exit on the same day. What I need is to recover the first entry and the last exit of the day.
Date hour Clock
------------------------
01/01/2017 09:00 1
01/01/2017 11:30 2
01/01/2017 17:00 2
02/01/2017 7:59 1
02/01/2017 16:00 1
I have this SQL query that works correctly.
SELECT
d.Date,
MIN(d.hour) as Entry,
MAX(dt.hour) as Exit
FROM
#temp1 AS d
LEFT JOIN
#temp1 AS dt ON d.Date = dt.Date
GROUP BY
d.Date
ORDER BY
Date DESC
BUT if I add 2 more columns to the query
SELECT
d.Date,
d.clock as ClockEntry, -- Aggregated column to display
MIN(d.hour) as Entry,
dt.clock as ClockExit, -- Aggregated column to display
MAX(dt.hour) as Exit
FROM
#temp1 AS d
LEFT JOIN
#temp1 AS dt ON d.Date = dt.Date
GROUP BY
d.Date
ORDER BY
Date DESC
I get this error:
Column '# temp1.clock' is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause.
I just need to group by the field "date", I do not want to add more conditions to the GROUP BY.. How could I solve it?
I want this result
DATE ClockEntry Entry ClockExit Exit
-------------------------------------------------------
01/01/2017 1 09:00 2 17:00
02/01/2017 1 7:59 1 16:00
So there is an easy way to do this - use 2 ranking functions:
Partitioned by date, Ordered by the hour ascending
Partitioned by date, Ordered by the hour descending
At that point the rows can be joined where they both have a value of 1, and the dates match.
I tend to use a CTE for this:
with temp2 (MinId, MaxId, Date, Hour, Clock)
AS
(
select ROW_NUMBER() Over (partition by date order by hour),
ROW_NUMBER() Over (partition by date order by hour desc),
*
from temp1
)
select distinct
d1.Date,
d1.Clock,
d1.Hour,
d2.Clock,
d2.Hour
FROM temp2 d1
LEFT JOIN temp2 d2
ON d1.Date = d2.Date -- dates match
AND d1.MinId=d2.MaxId -- minId=earliest record MaxId=latest record
WHERE d1.MinId=1
GROUP BY Method
If you know that only a single value will get out the clock column,
just aggregate the values with a MAX or MIN aggregate function for example like this :
SELECT
d.Date,
MIN(d.clock) as ClockEntry,
MIN(d.hour) as Entry,
MAX(dt.clock) as ClockExit,
MAX(dt.hour) as Exit
FROM #temp1 AS d
LEFT JOIN #temp1 AS dt
ON d.Date= dt.Date
GROUP BY d.Date
order by Date desc
Or if you have multiple clock values and want to see them all,
add them to the GROUP BY statement :
SELECT
d.Date,
d.clock as ClockEntry,
MIN(d.hour) as Entry,
dt.clock as ClockExit,
MAX(dt.hour) as Exit
FROM #temp1 AS d
LEFT JOIN #temp1 AS dt
ON d.Date= dt.Date
GROUP BY d.Date, d.clock, dt.clock
order by Date desc
ORDER BY Method
Use a cursor or a Common Table Expression for each date to get both first entry and last exit.
First Entry for a given date
SELECT TOP 1 d.date, d.clock as ClockEntry, d.hour as Entry
FROM #temp1 AS d
WHERE d.date = #myDate
ORDER BY d.hour ASC
Last Exit for a given date
SELECT TOP 1 d.date, d.clock as ClockExit, d.hour as Exit
FROM #temp1 AS d
WHERE d.date = #myDate
ORDER BY d.hour DESC
Reference :
GROUP BY Documentation
AGGREGATE FUNCTIONS

Select all days of the current week

Good Day! I am working on a chart where I need to display all the days of the current week to show the sales per Week. So far, I am able to display all the days of the current week, I'm just having a trouble in displaying the sales for each day of the week.Since there are no records in the database for the days of the week, it the TOTAL_SALES column should all return a Null value. Instead, it returns the total sales recorded in the database. Here is my Stored Procedure query so far.
WITH DAYSOFTHEWEEK AS
(
SELECT 0 DAY
UNION ALL
SELECT DAY + 1 FROM DAYSOFTHEWEEK WHERE DAY < 6
)
SELECT DATEADD(DAY, DAY, DATEADD(DAY, 2-DATEPART(WEEKDAY, CONVERT (date, GETDATE())), CONVERT (date, GETDATE()))) AS DAY_OF_THE_WEEK,
SUM([ORDER].NET_AMOUNT) AS TOTAL_SALES
FROM DAYSOFTHEWEEK, [ORDER]
GROUP BY DAYSOFTHEWEEK.DAY
I tried adding this condition statement,
WHERE DAYSOFTHEWEEK.DAY IN ([ORDER].ORDER_DATE)
But it returns this error
Operand type clash: date is incompatible with int
Can someone help me out on this?Is there a work around with the code that I already have? Thanks in advance!
What I think you're after is a SUM of each day's sales for the current week with NULL if there are no sales. The secret is to left join your date list onto your data:
-- Setup some fake sales data
WITH TestData(N, Order_Date, Net_Amount) AS (
SELECT 1 N, CAST(GETDATE() AS DATE) Order_Date, RAND() * 100 Net_Amount
UNION ALL
SELECT N+1 N, CAST(GETDATE()-N/5 AS DATE) Order_Date, RAND(CHECKSUM(NEWID())) * 100 Net_Amount FROM TestData
WHERE N < 20
)
SELECT TestData.Order_Date, TestData.Net_Amount INTO #Order FROM TestData
--Set the first day of the week (if required)
SET DATEFIRST 7 --Sunday
;WITH Days(N,DayOfTheWeek) AS (
SELECT 1 N, DATEADD(DAY, 1-DATEPART(WEEKDAY, GETDATE()), CONVERT(DATE,GETDATE())) DayOfTheWeek
UNION ALL
SELECT N+1 N,DATEADD(DAY, 1, DayOfTheWeek) DayOfTheWeek FROM Days
WHERE N < 7
)
SELECT d.DayOfTheWeek, SUM(Net_Amount) TotalAmount
FROM Days d
LEFT JOIN #Order ON d.DayOfTheWeek = Order_Date
GROUP BY d.DayOfTheWeek
DayOfTheWeek TotalAmount
------------ ----------------------
2016-08-07 219.036784917497
2016-08-08 273.319570812461
2016-08-09 271.148114731087
2016-08-10 194.780039228967
2016-08-11 NULL
2016-08-12 NULL
2016-08-13 NULL
Here is every day this week, starting at your datefirst date, which can be temporarily varied for the query with SET DATEFIRST if you need to have some other week start date
I think you have some sales table there that you haven't shown us, you need to join to that on date, then group by
WITH DAYSOFTHEWEEK AS
(
SELECT cast(dateadd(
day,
-datepart(weekday,getdate()) + 1 ,
GETDATE()
)
as date) [DAY], 0 as cnt
UNION ALL
SELECT dateadd(day,1,[DAY]), cnt + 1 FROM DAYSOFTHEWEEK WHERE cnt < 6
)
select DAYSOFTHEWEEK.[day], SUM([ORDER].NET_AMOUNT) AS TOTAL_SALES from daysoftheweek
JOIN
SalesTable on
CAST(SalesTable.SalesDate date) = DAYSOFTHEWEEK.[day]
GROUP BY DAYSOFTHEWEEK.[day]
A little over complicated for me:
To get name of the week use, for example
SELECT DATENAME(dw,getdate())
But you really need something like this:
SELECT ProductName,Sum(Sales) From NameOfTable GROUP BY
DATENAME(ww,salesDate)

Select missing months between 2 dates (in same year)

I'm trying to get all months in which no transaction is placed for the same year (If different years is not possible)
This is my query to get transactions between 2 dates, but don't know how can I select only months for which transaction in database is missing
SELECT *
FROM Installment
WHERE OrderId = 1
AND InstallmentDate
BETWEEN cast('8/02/2014' as date)
AND cast('12/25/2014' as date)
InstallmentId OrderId CustomerKey InstallmentAmount InstallmentDate
18 1 INS-1 3000 2014-09-03
92 1 INS-1 3000 2014-10-13
137 1 INS-1 3000 2014-11-05
in this case record for the 12th month and 8th month is missing, how can I get this with SQL Server Query ?
Update
select yymm.yy, yymm.mm
from (select distinct year(InstallmentDate) as yy, month(InstallmentDate) as mm
from Installment
where InstallmentDate BETWEEN '2014-09-02' and '2015-01-15'
) yymm left join
Installment i
on i.OrderId = 1 and
year(i.InstallmentDate) = yymm.yy and
month(i.InstallmentDate) = yymm.mm
where i.OrderId is not null;
Gordon's query is returning all the years and months from table between 2 dates, just by changing i.OrderId is null to i.OrderId is not null here is the out of his query
yy mm
2014 9
2014 10
2014 11
Expected Output (if possible)
yy mm
2014 12
2015 1
Using the following recursive CTE:
DECLARE #start DATE = '2014-09-02'
DECLARE #end DATE = '2015-01-15'
;WITH IntervalDates (date)
AS
(
SELECT #start
UNION ALL
SELECT DATEADD(MONTH, 1, date)
FROM IntervalDates
WHERE DATEADD(MONTH, 1, date)<=#end
)
SELECT YEAR(date) AS Year, MONTH(date) AS Month
FROM IntervalDates
you can get a list of all Years/Months between the two dates of interest:
Year Month
==============
2014 9
2014 10
2014 11
2014 12
2015 1
Using EXCEPT on the above CTE:
;WITH IntervalDates (date)
AS
(
SELECT #start
UNION ALL
SELECT DATEADD(MONTH, 1, date)
FROM IntervalDates
WHERE DATEADD(MONTH, 1, date)<=#end
)
SELECT YEAR(date) AS Year, MONTH(date) AS Month
FROM IntervalDates
EXCEPT
SELECT DISTINCT YEAR(InstallmentDate) AS yy, MONTH(InstallmentDate) AS mm
FROM Installment
WHERE OrderId = 1 AND InstallmentDate BETWEEN #start AND #end
yields the required result set:
Year Month
=============
2014 12
2015 1
To do this in SQL, you need to start with a list of months. Assuming you have at least one record for each month in the table, you can then get the missing dates easily using a subquery. The rest of the query is just a left join and checking for non-matches:
select yymm.yy, yymm.mm
from (select distinct year(InstallmentDate) as yy, month(InstallmentDate) as mm
from Installment
where InstallmentDate BETWEEN '2014-09-02' and '2015-01-15'
) yymm left join
Installment i
on i.OrderId = 1 and
year(i.InstallmentDate) = yymm.yy and
month(i.InstallmentDate) = yymm.mm
where i.OrderId is null;
Simplest way I can think of is to have a date dimension table that contains (at least) date, and 1st of month then. For creating one take a look at something like https://dba.stackexchange.com/questions/74957/best-approach-for-populating-date-dimension-table , although that one doesn't have firstOfMonthDate in it as my example show but the idea is the same.
then your query becomes
SELECT DISTINCT
firstOfMonthDate
FROM
dateRef dr
LEFT OUTER JOIN
InstallmentDate i ON dr.date = i.InstallmentDate AND i.OrderId = 1
WHERE
i.InstallmentDate IS NULL
AND
dr.date BETWEEN #startDate and #endDate
change firstOfMonthDate for fiscal month etc. as required. This would work across any range of dates you have in your table so different years wouldn't be an issue.
Try the below script. I retrieve all dates between the specified dates and use a LEFT JOIN to get those which are not present in your table:
DECLARE #startDate AS DATETIME, #endDate AS DATETIME
DECLARE #dates AS TABLE (CurrentDate DATETIME)
SET #startDate = '2014-01-01'
SET #endDate = '2014-01-31';
with GetDates AS
(
SELECT #startDate AS TheDate
UNION ALL
SELECT DATEADD("DD", 1, TheDate) FROM GetDates
WHERE TheDate < #endDate
) INSERT INTO #dates SELECT TheDate FROM GetDates
OPTION (MAXRECURSION 0)
SELECT DISTINCT YEAR(d.CurrentDate), MONTH(d.CurrentDate) FROM #dates d
LEFT JOIN InstallmentDate i ON i.InstallmentDate BETWEEN #startDate AND #endDate AND OrderId = 1
WHERE i.InstallmentDate IS NULL
Hope this helps...

Get latest record for each day for last n days using MS Sql Server

CurrencyId LeftCurrencyId RightCurrencyId ExchangeRateAt ExchangeRate
1 1 5 2013-06-27 00:51:00.000 39.0123
2 3 5 2013-06-26 01:54:00.000 40.0120
3 1 5 2013-06-26 00:51:00.000 49.0143
4 3 5 2013-06-25 14:51:00.000 33.3123
5 3 5 2013-06-25 06:51:00.000 32.0163
6 1 5 2013-06-25 00:08:00.000 37.0123
I need latest record for each day for last n days based on combination of leftcurrencyid and rightcurrencyid.
Here's one option:
with TopPerDay as
(
select *
, DayRank = row_number() over (partition by LeftCurrencyId, RightCurrencyId, cast(ExchangeRateAt as date)
order by ExchangeRateAt desc)
from ExchangeRate
)
select CurrencyId,
LeftCurrencyId,
RightCurrencyId ,
ExchangeRateDay = cast(ExchangeRateAt as date),
ExchangeRateAt ,
ExchangeRate
from TopPerDay
where DayRank = 1
order by LeftCurrencyId,
RightCurrencyId,
ExchangeRateDay
SQL Fiddle with demo.
It groups by LeftCurrencyId, RightCurrencyId, and ExchangeRateAt day without the time component, then takes the latest record in the day for all those groups.
You don't mention whether you want N days back is from the present day or an unspecified date, but you can add this using a WHERE clause when selecting from the ExchangeRate table in the CTE definition.
Here are my two cents
Select ExchangeRateAt , * from Table1 where ExchangeRateAt in (Select max(ExchangeRateAt) from Table1 Group by cast( ExchangeRateAt as Date))
Order by ExchangeRateAt
Here 7 in the end is the last N days parameter (7 in this example)
with T1 as
(
select t.*,
cast(floor(cast([ExchangeRateAt] as float)) as datetime) as DatePart,
ROW_NUMBER() OVER (
PARTITION BY [LeftCurrencyId],
[RightCurrencyId],
cast(floor(cast([ExchangeRateAt] as float)) as datetime)
ORDER BY [ExchangeRateAt] DESC
) RowNumber
from t
), T2 as
(
select *,
ROW_NUMBER() OVER (PARTITION BY [LeftCurrencyId],
[RightCurrencyId]
ORDER BY DatePart DESC
) as RN
from T1 where RowNumber=1
)
select [CurrencyId],
[LeftCurrencyId],
[RightCurrencyId],
[ExchangeRateAt],
[ExchangeRate],
DatePart
from T2 where RN<=7
SQLFiddle demo

Resources