Show 0 if null in query - sql-server

I am running a sql query that is omitting the day if the return count is 0. I want my query to return the day and a 0 count if the the count is 0. Snare I have is that if 0 were sold for the day, the day is omitted from my return results.
SELECT ISNULL([day],0) As [day], COUNT(ISNULL(Sold,0)) As [Sold]
FROM productionInfo

You're drawing information from a single table, productionInfo. If productionInfo has no rows with that date information (because there are no widgets sold on that date), how does it know what dates to use?
You might want to look at using a Numbers Table to get a row for each day of the month/year, then join that to productionInfo so you have a day value available, even if there was no production that day.

This will give you a dates table:
CREATE FUNCTION dbo.DatesTable (#startDate DATETIME, #endDate DATETIME)
RETURNS #retTable TABLE (DateValue DATETIME)
AS
BEGIN
DECLARE #currentDate DATETIME
SET #currentDate = #startDate
WHILE (DATEDIFF(dd, #currentDate, #endDate) >= 0)
BEGIN
INSERT INTO #retTable VALUES (#currentDate)
SET #currentDate = DATEADD(dd, 1, #currentDate)
END
RETURN
END
Then your query will look like:
SELECT dt.DateValue AS [day], COUNT(Sold) AS [Sold]
FROM dbo.DatesTable('2-1-2014', '2-10-2014') dt
LEFT JOIN productionInfo pi ON pi.day = dt.DateValue
GROUP BY dt.DateValue

Related

Find out the quarter based on the financial year in SQL Server stored procedure

I have two tables, one is Period and the other is UserTarget.
Period table is:
QuarterNo StartDate EndDate
-----------------------------------------
1 2018-04-01 2018-06-30
2 2018-07-01 2018-09-30
3 2018-10-01 2018-12-31
4 2019-01-01 2019-03-31
UserTarget table is :
USERID YEAR QTR AMOUNT
---------------------------------
akshay 2019 1 200
Right now I am taking the qtr no from period table. Now I don't need take qtr no from period table. I want it from stored procedure based on year is entered in usertarget table
Existing stored procedure :
ALTER PROCEDURE [dbo].[GetQuarterlyTargetData]
#Userid VARCHAR(50)
AS
BEGIN
DECLARE #QuarterNumber VARCHAR(50)
DECLARE #SetTarget DECIMAL(10);
DECLARE #StartDate DATE
DECLARE #EndDate DATE
SELECT
#QuarterNumber = p.QuarterNo,
#SetTarget = AMOUNT
FROM
PERIOD p
LEFT OUTER JOIN
USERTARGETS s ON p.QuarterNo = s.QTR
WHERE
StartDate <= GETDATE() AND EndDate >= GETDATE()
SELECT
#StartDate = StartDate,
#EndDate = EndDate
FROM
PERIOD
WHERE
QuarterNo = #QuarterNumber
From this procedure I am getting the start date and end date for quarter but I don't want to modify in period table every time when I want to check previous years data.
I believe that the term you are looking for is fiscal year. It's where the company year is different than the calendar year.
Note that many people recommend using a lookup table instead of calculating it. Date matches can be difficult for SQL to optimize.
Here's one way to do it. Finding the fiscal year and quarter would probably be good to put in a table function.
DECLARE #userTarget TABLE (UserId VARCHAR(20), Year INT, Quarter INT, Amount INT)
INSERT INTO #userTarget
VALUES
('akshay', 2018, 4, 150)
,('akshay', 2019, 1, 200)
SELECT
s.UserId
,s.Amount
,FY.FiscalYear
,FQ.FiscalQuarter
FROM
(
SELECT
--DATEFROMPARTS(2019, 2, 23)
GETDATE()
AS reportDate
) AS ReportDate
CROSS APPLY (
SELECT
CASE WHEN MONTH(ReportDate.reportDate) < 4 THEN YEAR(ReportDate.reportDate) - 1 -- Fiscal Year begins in April
ELSE YEAR(ReportDate.reportDate)
END AS FiscalYear
) AS FY
CROSS APPLY (
SELECT
DATEDIFF(QUARTER, DATEFROMPARTS(FY.FiscalYear, 4, 1), ReportDate.reportDate) + 1 AS FiscalQuarter
) AS FQ
INNER JOIN #userTarget s
ON s.Year = FY.FiscalYear
AND s.Quarter = FQ.FiscalQuarter
Also, be careful with end dates. last_day >= GETDATE() does not include the last day. Take as an example the end of last quarter, it would calculate it as '2019-03-31 00:00' >= '2019-03-31 08:20' which is false when you want it to be true.
After thinking I come up to this solutions
ALTER PROCEDURE [dbo].[Dashboard_GetQuarterlyTargetData]
#Userid varchar(50)
AS
Begin
DECLARE #QuarterNumber varchar(50)
DECLARE #SetTarget decimal(10);
DECLARE #AchievedTarget decimal(10);
DECLARE #TargetProgress decimal(10);
DECLARE #RemainingTarget decimal(10);
DECLARE #StartDate Date
DECLARE #EndDate Date
DECLARE #Year as int
Select #QuarterNumber = QTR,#Year=YEAR,#SetTarget=AMOUNT from USERTARGETS where USERID=#Userid
if(#QuarterNumber = 1)
begin
SELECT #StartDate = DATEFROMPARTS(#year,4,1), #EndDate=DATEFROMPARTS(#year,6,30)
End
else if(#QuarterNumber = 2)
begin
SELECT #StartDate = DATEFROMPARTS(#year,7,1), #EndDate=DATEFROMPARTS(#year,9,30)
End
else if(#QuarterNumber = 3)
begin
SELECT #StartDate = DATEFROMPARTS(#year,10,1), #EndDate=DATEFROMPARTS(#year,12,31)
End
else if(#QuarterNumber = 4)
begin
SELECT #StartDate = DATEFROMPARTS(#year,1,1), #EndDate=DATEFROMPARTS(#year,3,31)
End

Creating a time dimension with specific dates on SQL

I'm making an analysis project on football transfers. I have a model with one Fact Table called FactTransfers and I need to link it to a time dimension but i need a specific range of dates, namely the dates where it's possible to transfer players (from June 1st to September 1st and from January 1st to January 31st).
I have seen some posts related to the matter but they all have code and attributes that I don't need.
Basically what i want is:
Date as primary key,
Day of the month,
Name of the month,
Transfer window (summer or winter),
Year.
I'm not too familiarized with sql code and I have spent hours trying to figure it out without the results I need.
Thank you in advance for all your help!
Here is the code to create and populate your Dim table for Dates. Hope this helps.
CREATE TABLE [dbo].[DimDate]
(
[DateKey] INT primary key,
[Date] DATETIME,
[DayofMonth] TINYINT, -- Field will hold day number of Month
[NameofMonth] VARCHAR(9),--January, February etc
[TransferWindow] VARCHAR(20), -- Summer & Winter
)
--Specify Start Date and End date here
--Value of Start Date Must be Less than Your End Date
DECLARE #StartDate DATETIME = '01/01/2015' --Starting value of Date Range
DECLARE #EndDate DATETIME = '12/31/2025' --End Value of Date Range
DECLARE #CurrentDate AS DATETIME = #StartDate
WHILE #CurrentDate < #EndDate
BEGIN
INSERT INTO [dbo].[DimDate]
SELECT
CONVERT (char(8),#CurrentDate,112) as DateKey,
#CurrentDate AS Date,
DATEPART(DD, #CurrentDate) AS [DayOfMonth],
DATENAME(MM, #CurrentDate) AS [MonthName],
CASE WHEN (MONTH(#CurrentDate) BETWEEN 6 AND 8) OR ( MONTH(#CurrentDate) =9 AND DATEPART(DD, #CurrentDate)=1) THEN 'Summer'
WHEN MONTH(#CurrentDate) =1 THEN 'Winter'
ELSE ''
END AS [TransferWindow]
SET #CurrentDate = DATEADD(DD, 1, #CurrentDate)
END
SELECT * FROM [DimDate]
--DROP TABLE [DimDate]

SQL Server - Generate date field data in where clause

My query is
Select * from Orders
where ordersdate Between '2013-10-10 00:00:00.00' and '2013-10-10 23:59:59.997'
I need to run this query on daily basis and it should be previous day dates. so how to generate dates in above format is what im struggling with.
Select *
from Orders
where ordersdate >= cast(dateadd(d, -1, getdate()) as date)
and ordersdate < cast(getdate() as date)
Instead of the additonal time you can use >= yesterday's date and < today.
DECLARE #StartDate DATETIME, #EndDate DATETIME
/* set the end date to midnight of the previous day */
SET #EndDate = CONVERT(DATETIME, CONVERT(VARCHAR(10), GETDATE(), 121), 121)
/* set the start date to 1 day previously (thereby getting midnight to midnight) */
SET #StartDate = DATEADD(day, -1, #EndDate)
/* you could use between, but orders placed at midnight might be counted twice (on subsequent day reports) */
Select * from Orders where ordersdate Between #StartDate AND #EndDate
/* or you could use >= and < to guarantee that a order placed at midnight will only be counted once */
Select * from Orders where ordersdate >= #StartDate AND ordersdate < #EndDate
select *
from orders
where datediff(d, ordersdate, getdate()) = 1
/edit/
As per Jason Musgrove comment - such query is easy to write and understand, but depend on rows count may become quite inefficent.
taking a slightly different approach (but probably not as good as Arvo's Answer) and there are quicker ways to perform the cast
select *
from orders
where cast(ordersdate as varchar(11)) = cast( dateadd(dd,-1,getdate()) as varchar(11) )

Count days in date range with set of exclusions which may overlap

Given the following example query, what is a sound and performant approach to counting the total days in a date range when also given a set of ranges to exclude, given that those ranges may have dates which overlap?
More simply, I have a table with a set of date ranges where the billing is turned off, I start with a date range (say Jan1 - Jan31) and I need to determine how many billable days occured in that range. Simply a datediff of the days minus a sum of the datediff on the disabled days. However, there is a chance that the disabled date ranges overlap, ie disabled Jan5-Jan8 in one record and Jan7-Jan10 in another record - thus a simple sum would double count Jan7. What is the best way to exclude these overlaps and get an accurage count.
Declare #disableranges table (disableFrom datetime, disableTo datetime)
insert into #disableranges
select '01/05/2013', '01/08/2013' union
select '01/07/2013', '01/10/2013' union
select '01/15/2013', '01/20/2013'
declare #fromDate datetime = '01/01/2013'
declare #toDate datetime = '01/31/2013'
declare #totalDays int = DATEDIFF(day,#fromDate,#toDate)
declare #disabledDays int = (0 /*not sure best way to calc this*/)
select #totalDays - #disabledDays
You can use a recursive CTE to generate dates between #dateFrom and #dateTo. Then compare the dates with the ranges, and find all dates that are in any range. Finally, count the number of rows in the result to get the count of disabled dates (DEMO):
-- recursive CTE to generate dates
;with dates as (
select #fromDate as date
union all
select dateadd(day, 1, date)
from dates
where date < #toDate
)
-- join with disable ranges to find dates in any range
, disabledDates as (
select date from dates D
left join #disableranges R
on D.date >= R.disableFrom and d.Date < R.disableTo
group by date
having count(R.disablefrom) >= 1
)
-- count up the total disabled dates
select #disabledDays=count(*) from disabledDates;
Tried this and working okay as far as I am concerned.
Declare #disableranges table (disableFrom datetime, disableTo datetime)
insert into #disableranges
select '01/05/2013', '01/08/2013' union
select '01/07/2013', '01/10/2013' union
select '01/15/2013', '01/20/2013'
declare #fromDate datetime = '01/01/2013'
declare #toDate datetime = '01/31/2013'
declare #totalDays int = DATEDIFF(day,#fromDate,#toDate) + 1 /*Without +1 it is giving 30 instead of 31*/
declare #disabledDays int = (0 /*not sure best way to calc this*/)
/*Fill temporary table with the given date range.*/
SELECT DATEADD(DAY, nbr - 1, #fromDate) TempDate INTO #Temp
FROM ( SELECT ROW_NUMBER() OVER ( ORDER BY c.object_id ) AS Nbr
FROM sys.columns c
) nbrs
WHERE nbr - 1 <= DATEDIFF(DAY, #fromDate, #toDate)
/*Check how many dates exists in the disableranges table*/
SELECT #disabledDays=count(*) from #Temp t WHERE
EXISTS(SELECT * FROM #disableranges
WHERE t.TempDate BETWEEN disableFrom AND DATEADD(d, -1, disableTo))
select #totalDays /*Output:31*/
select #disabledDays /*Output:10*/
select #totalDays - #disabledDays /*Output:21*/
drop table #Temp
Taken help from the answer https://stackoverflow.com/a/7825036/341117 to fill table with date range

Sybase get today result, two way comparison

I don't know how to test the performance of these two way of getting what i want.
select *
from table
where column like '%'+left(getdate(),11)+"%
select *
from table
where
DATEPART (DD, column) = DATEPART (DD, GETDATE())
AND DATEPART (MM, column) = DATEPART (MM, GETDATE())
AND DATEPART (YY, column) = DATEPART (YY, GETDATE())
Typically in SQL you can just loop through executing the statement however many times and calculating the time to execute for each statement. Do this a few times to be sure and compare the results.
declare #startTime datetime
declare #endTime datetime
declare #execution BIGINT
SET #execution = 0
set #startTime = GETDATE()
while (#execution < 1000000)
begin
--TestSyntax goes here
SET #execution = #execution + 1
end
set #endTime = GETDATE()
SELECT DATEDIFF(ms, #endTime, #startTime) AS 'TimeToExecute'
Also know, string comparisons are typically slower in all languages.

Resources