Related
I have a table with columns like this:
I want to know the average compliance rate for every question for that period.
I am passing start dates and end dates as parameters to query.
So if I want for two periods I am passing #StartDate (e.g. 1/6/17) and #EndDate (e.g. 30/6/17) for first period and #StartDate2 (1/10/17) and #EndDate2 (31/10/17) for second period.
My SQL query is:
;WITH tmpTab AS
(
SELECT
question,
SUM((CASE WHEN AnswerValue = 1 THEN 1 ELSE 0 END)) Met,
SUM((CASE WHEN AnswerValue = 3 THEN 1 ELSE 0 END)) NA,
SUM((CASE WHEN (ISNULL(AnswerValue,3) <> 3) THEN 1 ELSE 0 END)) MetNotMet,
DATENAME(DAY, #StartDate) + ' ' + DATENAME(MONTH, #StartDate) + ' ' +
DATENAME(YEAR, #StartDate) + ' To ' + DATENAME(DAY,#EndDate) + ' ' +
DATENAME(MONTH, #EndDate) + ' ' + DATENAME(YEAR, #EndDate) AS RepMonthAndYear
FROM
tableA
WHERE
startdate >= #StartDate AND endate <= #EndDate
GROUP BY
Question
UNION ALL
SELECT
question,
SUM((CASE WHEN AnswerValue = 1 THEN 1 ELSE 0 END)) Met,
SUM((CASE WHEN AnswerValue = 3 THEN 1 ELSE 0 END)) NA,
SUM((CASE WHEN (ISNULL(AnswerValue,3) <> 3) THEN 1 ELSE 0 END)) MetNotMet,
DATENAME(DAY,#StartDate2)+' '+DATENAME(MONTH,#StartDate2)+' '+DATENAME(YEAR,#StartDate2)+ ' To ' + DATENAME(DAY,#EndDate2)+' '+DATENAME(MONTH,#EndDate2)+' '+DATENAME(YEAR,#EndDate2) AS RepMonthAndYear
FROM
tableA
WHERE
startdate >= #StartDate2 AND endate <= #EndDate2
GROUP BY
Question
)
SELECT
Question, Met, NA, MetNotMet,
CASE WHEN (Met) = 0 THEN 0 ELSE ROUND(((CONVERT(FLOAT,(Met))/(MetNotMet))* 100),4) END as CompRate
FROM
tmpTab
In this SQL query, I need to group by RepMonthAndYear column also which I can not do as it is a calculated column. I get an error "invalid column".
And if I use this GROUP BY clause:
(DATENAME(DAY,#StartDate2)+' '+DATENAME(MONTH,#StartDate2)+' '+DATENAME(YEAR,#StartDate2)+ ' To ' + DATENAME(DAY,#EndDate2)+' '+DATENAME(MONTH,#EndDate2)+' '+DATENAME(YEAR,#EndDate2) )
I get this error:
Each GROUP BY expression must contain at least one column that is not an outer reference.
How can I solve this problem?
Is there any other way to know average rate group by particular periods?
Given the following Test Data and your SQL I get what you discibe.
CREATE TABLE tableA (
question VARCHAR(50),
AnswerValue INT,
startdate DATE,
endate DATE
);
INSERT INTO tableA VALUES ('Question A', 1, '2017-06-25', '2017-06-26');
INSERT INTO tableA VALUES ('Question A', 1, '2017-06-27', '2017-06-27');
INSERT INTO tableA VALUES ('Question A', 2, '2017-06-27', '2017-06-27');
INSERT INTO tableA VALUES ('Question B', 1, '2017-06-11', '2017-06-12');
INSERT INTO tableA VALUES ('Question B', 2, '2017-06-13', '2017-06-13');
INSERT INTO tableA VALUES ('Question B', 1, '2017-06-13', '2017-06-13');
INSERT INTO tableA VALUES ('Question C', 1, '2017-06-20', '2017-06-20');
INSERT INTO tableA VALUES ('Question C', 1, '2017-06-23', '2017-06-23');
INSERT INTO tableA VALUES ('Question D', 2, '2017-06-01', '2017-06-01');
INSERT INTO tableA VALUES ('Question E', 1, '2017-10-11', '2017-10-11');
INSERT INTO tableA VALUES ('Question E', 1, '2017-10-15', '2017-10-15');
INSERT INTO tableA VALUES ('Question F', 1, '2017-10-20', '2017-10-20');
INSERT INTO tableA VALUES ('Question F', 2, '2017-10-20', '2017-10-20');
INSERT INTO tableA VALUES ('Question F', 2, '2017-10-20', '2017-10-20');
INSERT INTO tableA VALUES ('Question G', 1, '2017-10-26', '2017-10-26');
INSERT INTO tableA VALUES ('Question H', 1, '2017-10-26', '2017-10-26');
INSERT INTO tableA VALUES ('Question H', 2, '2017-10-26', '2017-10-26');
INSERT INTO tableA VALUES ('Question I', 1, '2017-10-26', '2017-10-26');
Here the outcome of your Query:
Question A 2 0 3 66,6667
Question B 2 0 3 66,6667
Question C 2 0 2 100
Question D 0 0 1 0
Question E 2 0 2 100
Question F 1 0 3 33,3333
Question G 1 0 1 100
Question H 1 0 2 50
Question I 1 0 1 100
I could simplify your SQL and have removed some code duplicates, maybe that was your intended question.
DECLARE #StartDate DATE = '2017-06-01';
DECLARE #EndDate DATE = '2017-06-30';
DECLARE #StartDate2 DATE = '2017-10-01';
DECLARE #EndDate2 DATE = '2017-10-30';
WITH
periods AS (
SELECT #StartDate as startdate,
#EndDate as enddate
UNION
SELECT #StartDate2 as startdate,
#EndDate2 as enddate
)
SELECT t.Question, t.Met, t.NA, t.MetNotMet,
CASE WHEN (t.Met) = 0 THEN 0 ELSE ROUND(((CONVERT(FLOAT,(t.Met))/(t.MetNotMet))* 100),4) END as CompRate
FROM (SELECT question,
SUM((CASE WHEN AnswerValue = 1 THEN 1 ELSE 0 END)) Met,
SUM((CASE WHEN AnswerValue = 3 THEN 1 ELSE 0 END)) NA,
SUM((CASE WHEN (ISNULL(AnswerValue,3) <> 3) THEN 1 ELSE 0 END)) MetNotMet
FROM tableA AS a
JOIN ( SELECT p.startdate,
p.enddate,
DATENAME(DAY, p.startdate) + ' ' + DATENAME(MONTH, p.startdate) + ' ' +
DATENAME(YEAR, p.startdate) + ' To ' + DATENAME(DAY, p.enddate) + ' ' +
DATENAME(MONTH, p.enddate) + ' ' + DATENAME(YEAR, p.enddate) AS RepMonthAndYear
FROM periods p ) AS p ON a.startdate >= p.startdate AND a.endate <= p.enddate
GROUP BY a.Question, p.RepMonthAndYear) AS t
Hello I am working on a dataset for a report in SSRS
and I have a query which gives the total requests in the backlog :
SELECT
COUNT(*) as NB
FROM p_rqt WITH (NOLOCK)
INNER JOIN p_cpy WITH (NOLOCK) ON p_cpy.CpyInCde = p_rqt.OrigCpyInCde
WHERE
CpyTypInCde IN (27, 31)
AND p_rqt.RqtNatInCde IN (74, 75, 76)
AND HeadRqtInCde = 0
AND p_rqt.OrigCpyInCde LIKE CASE WHEN #Client = 0 THEN '%' ELSE #Client END
AND ((RcvDte < DATEADD(day, 1, #DateDeb) AND RqtEndDte IS NULL) OR
(RcvDte < DATEADD(day, 1, #DateDeb) AND RqtEndDte > DATEADD(day, 1, #DateDeb)))
and I want to retrieve the total amount left per day.
I tried lot of things like this :
SELECT CONVERT(date,rcvdte,103), count(*) as nb
FROM p_rqt p WITH (NOLOCK)
INNER JOIN p_cpy WITH (NOLOCK) ON p_cpy.CpyInCde = p.OrigCpyInCde
WHERE
CpyTypInCde IN (27, 31)
AND p.RqtNatInCde IN (74, 75, 76)
AND HeadRqtInCde = 0
AND ((RcvDte < DATEADD(day, 1, '20170901') AND RqtEndDte IS NULL) OR (RcvDte < DATEADD(day, 1, '20170901') AND RqtEndDte > DATEADD(day, 1, '20170901')))
group by CONVERT(date,rcvdte,103)
order by CONVERT(date,rcvdte,103)
I tried inner join subqueries, Sum and other stuff
but all I can manage to do is to have the number of records added per day
and I want something like this :
date: NB:
01/01/2017 1950
02/01/2017 1954 (+4 items)
03/01/2017 1945 (-9 items)
Thank you
Use LAG:
WITH cte AS (
SELECT
CONVERT(date, rcvdte, 103) AS date,
COUNT(*) AS nb
FROM p_rqt p WITH (NOLOCK)
INNER JOIN p_cpy WITH (NOLOCK)
ON p_cpy.CpyInCde = p.OrigCpyInCde
WHERE
CpyTypInCde IN (27, 31) AND
p.RqtNatInCde IN (74, 75, 76) AND
HeadRqtInCde = 0 AND
((RcvDte < DATEADD(day, 1, '20170901') AND RqtEndDte IS NULL) OR (RcvDte < DATEADD(day, 1, '20170901') AND RqtEndDte > DATEADD(day, 1, '20170901')))
GROUP BY CONVERT(date, rcvdte, 103)
ORDER BY CONVERT(date, rcvdte, 103)
)
SELECT
t1.date,
(SELECT SUM(t2.nb) FROM cte t2 WHERE t2.date <= t1.date) AS nb,
CASE WHEN t1.nb - LAG(t1.nb, 1, t1.nb) OVER (ORDER BY t1.date) > 0
THEN '(+' + (t1.nb - LAG(t1.nb, 1, t1.nb) OVER (ORDER BY t1.date)) + ' items)'
ELSE '(' + (t1.nb - LAG(t1.nb, 1, t1.nb) OVER (ORDER BY t1.date)) + ' items)'
END AS difference
FROM cte t1
ORDER BY t1.date;
So i found a solution but it is really slow,
i still post the answer anyway
DECLARE #Tb TABLE ( Colonne1 Datetime, Colonne2 INT )
DECLARE #Debut Datetime = '01/09/2017'
WHILE #Debut < '13/09/2017'
BEGIN
DECLARE #Compteur int = (
SELECT
COUNT(1) NB
FROM p_rqt WITH (NOLOCK)
INNER JOIN p_cpy WITH (NOLOCK) ON p_cpy.CpyInCde = p_rqt.OrigCpyInCde
WHERE
CpyTypInCde IN (27, 31)
AND p_rqt.RqtNatInCde IN (74, 75, 76)
AND HeadRqtInCde = 0
AND p_rqt.OrigCpyInCde LIKE '%'
AND (
(RcvDte < #Debut AND RqtEndDte IS NULL)
OR
(RcvDte < #Debut AND RqtEndDte > #Debut)
)
)
INSERT INTO #Tb (Colonne1, Colonne2) VALUES (#Debut, #Compteur)
SET #Debut = DATEADD(day, 1, #Debut)
IF #Debut > '13/09/2017'
BREAK
ELSE
CONTINUE
END
SELECT * FROM #Tb
I have the following query which counts the number of items created on a particular date in the last 10 days
SELECT
CONVERT (DATE, CreatedDate_6258638D_B885_AB3C_E316_D00782B8F688) AS 'Logged Date',
Count (*) AS 'Total'
FROM
MTV_System$WorkItem$Incident
WHERE
CreatedDate_6258638D_B885_AB3C_E316_D00782B8F688 >= DATEADD(DAY, DATEDIFF(DAY, 0, Getdate()) - 10, 0)
GROUP BY
CONVERT(DATE, CreatedDate_6258638D_B885_AB3C_E316_D00782B8F688)
How do I get this to show the dates which have no values present (i.e. get every date value for the last 10 days, return the count if there is data or 0 if none). Using SQL Server 2012.
You can write a recursive cte to get the date for the last 10 days into a table as follows:
WITH TableA (StartDate) AS (SELECT DATEADD(DAY, DATEDIFF(DAY, 0, Getdate()) - 10, 0)),
q as (
SELECT StartDate
, Number = 0
FROM TableA
UNION ALL
SELECT DATEADD(d,1,StartDate)
, Number = Number + 1
FROM q
WHERE 10 > Number )
Then join q with your original query, to get a row for every date.
select q.StartDate, yourtable.Total from q
left join (
SELECT
CONVERT (DATE, CreatedDate_6258638D_B885_AB3C_E316_D00782B8F688) AS 'Logged Date',
Count (*) AS 'Total'
FROM
MTV_System$WorkItem$Incident
WHERE
CreatedDate_6258638D_B885_AB3C_E316_D00782B8F688 >= DATEADD(DAY, DATEDIFF(DAY, 0, Getdate()) - 10, 0)
GROUP BY
CONVERT(DATE, CreatedDate_6258638D_B885_AB3C_E316_D00782B8F688)
) as yourtable on [Logged Date] = q.StartDate
Similar to BeanFrog's answer but a little shorter
-- sample data for testing
declare #MTV_System$WorkItemIncident table (
[CreatedDate_6258638D_B885_AB3C_E316_D00782B8F688] DATE,
[Total] INT
);
INSERT INTO #MTV_System$WorkItemIncident VALUES ('2015-11-23', 23);
INSERT INTO #MTV_System$WorkItemIncident VALUES ('2015-11-21', 21);
INSERT INTO #MTV_System$WorkItemIncident VALUES ('2015-11-30', 30);
-- now the query
WITH TableA (LoggedDate) AS (
SELECT TOP 10 CONVERT (DATE, DATEADD(DAY, number * -1, GETDATE())) AS 'LoggedDate'
FROM master.dbo.spt_values
WHERE name IS NULL
)
SELECT TableA.[LoggedDate],
SUM(ISNULL(Data.Total, 0)) AS 'LoggedCount'
FROM TableA
LEFT JOIN #MTV_System$WorkItemIncident AS Data ON CONVERT (DATE, CreatedDate_6258638D_B885_AB3C_E316_D00782B8F688) = TableA.[LoggedDate]
GROUP BY TableA.[LoggedDate]
Not that there is anything wrong with BeanFrog's answer, but if you don't want to use a recursive cte you could do this:
CREATE TABLE MTV_System$WorkItem$Incident (id int PRIMARY KEY, CreatedDate_6258638D_B885_AB3C_E316_D00782B8F688 datetime)
INSERT INTO MTV_System$WorkItem$Incident VALUES (1, '20151201')
INSERT INTO MTV_System$WorkItem$Incident VALUES (2, '20151126')
INSERT INTO MTV_System$WorkItem$Incident VALUES (3, '20151127')
INSERT INTO MTV_System$WorkItem$Incident VALUES (4, '20151127')
SELECT
ReportDate AS 'Logged Date',
Count (CreatedDate_6258638D_B885_AB3C_E316_D00782B8F688) AS 'Total'
FROM (
SELECT DATEADD(DAY, DATEDIFF(DAY, 0, Getdate()) - 0, 0) AS ReportDate
UNION SELECT DATEADD(DAY, DATEDIFF(DAY, 0, Getdate()) - 1, 0)
UNION SELECT DATEADD(DAY, DATEDIFF(DAY, 0, Getdate()) - 2, 0)
UNION SELECT DATEADD(DAY, DATEDIFF(DAY, 0, Getdate()) - 3, 0)
UNION SELECT DATEADD(DAY, DATEDIFF(DAY, 0, Getdate()) - 4, 0)
UNION SELECT DATEADD(DAY, DATEDIFF(DAY, 0, Getdate()) - 5, 0)
UNION SELECT DATEADD(DAY, DATEDIFF(DAY, 0, Getdate()) - 6, 0)
UNION SELECT DATEADD(DAY, DATEDIFF(DAY, 0, Getdate()) - 7, 0)
UNION SELECT DATEADD(DAY, DATEDIFF(DAY, 0, Getdate()) - 8, 0)
UNION SELECT DATEADD(DAY, DATEDIFF(DAY, 0, Getdate()) - 9, 0)
UNION SELECT DATEADD(DAY, DATEDIFF(DAY, 0, Getdate()) - 10, 0)
) AS Dates
LEFT JOIN MTV_System$WorkItem$Incident ON ReportDate = CONVERT(DATE, CreatedDate_6258638D_B885_AB3C_E316_D00782B8F688)
GROUP BY ReportDate
BeanFrog's answer has the advantage of being able to easily change the number of days though.
I have a SQL Server table:
CREATE TABLE tblExample
(
ID int,
Name nvarchar(256),
Date datetime,
IsAnual bit
)
This is a simplified example.
Now I scan the next 30 days from GETDATE(). If there is result, I insert information into another table:
WHERE DATEDIFF(dd, GETDATE(), Date) <= 30
Up to now there is no problem. But
WHERE IsAnual = 1
I must take into account their continuations. How can I do this?
Suppose that GETDATE() is 2013-10-22 and the column contains 2013-10-30, there is not problem. What if GETDATE() is 2014-10-28 and column contains 2013-10-30 AND IsAnual = 1?
Updated:
I found solution. I used recursive query.
CREATE TABLE tblExample
(
ID int IDENTITY(1,1) PRIMARY KEY NOT NULL,
Name nvarchar(256),
Date datetime,
IsAnual bit
)
And inserted some rows:
INSERT INTO tblExample
(Name, Date, IsAnual)
VALUES
('A', '2012-11-01', 1),
('B', '2013-11-01', 0),
('C', '2013-01-01', 1)
And final section is properly working query:
WITH TempTable AS
(
SELECT
e.ID,
e.Name,
e.Date,
e.IsAnual
FROM tblExample AS e
UNION ALL
SELECT
e.ID,
e.Name,
DATEADD(yy, 1, t.Date),
e.IsAnual
FROM tblExample AS e
INNER JOIN TempTable AS t
ON e.ID = t.ID
WHERE e.IsAnual = 1
AND DATEDIFF(yy, t.Date, DATEADD(yy, 1, GETDATE())) > 0
)
SELECT
*
FROM TempTable
WHERE DATEDIFF(dd, GETDATE(), Date) BETWEEN 0 AND 30
Results here:
14 B 01.11.2013 False
13 A 01.11.2013 True
WHERE DATEDIFF(dd, GETDATE(),
CASE
WHEN IsAnnual = 0 THEN Date
WHEN IsAnnual = 1 THEN DATEADD(year,DATEDIFF(year,Date,GETDATE()),Date)
END
) <= 30
The expression DATEADD(year,DATEDIFF(year,Date,GETDATE()),Date) will give you the date provided in the Date column but with its year set to the current year.
I think that's what you were asking for.
It should be noted, however, that the above will not be able to leverage any indexes on Date, so may not provide the absolute best performance on a large table.
(My initial attempt had the CASE expression incorrect, but it's hopefully correct now)
WHERE DATEDIFF( DAY ,DATEADD(YEAR,(1753 -DATEPART(YEAR ,GETDATE())) *IsAnual ,GETDATE()) ,DATEADD(YEAR ,(1753 -DATEPART(YEAR ,Date)) *IsAnual ,Date)) BETWEEN 0 AND 30
I need to get the total amount of the last 3 transactions of each customer from the previous month. Let's say today is 2012/1/31.
Please provide a step by step answer regarding why you use the approach you are using.
For example, here is the answer that I thought of. It might be wrong.
Create a cursor to iterate through the CustomerTransaction table group by CustomerName
Create the inner query to get the last 3 transactions of a customer for the previous month (using select top 3), insert it to a temporary table
Select the result within the temporary table and get the sum(Amount) and group it by the CustomerName.
So I have a CustomerTransaction table with these columns:
ID, CustomerName, Amount, TransactionDate
Here is a script if you need it. I am using it to test the result.
insert into Test.dbo.CustomerTransaction (CustomerName, Amount, TransactionDate)
values ('John', 100.0, '2011-12-31'),
('John', 100.0, '2011-12-30'),
('John', 100.0, '2011-12-29'),
('John', 100.0, '2011-12-28'),
('Boyd', 100.0, '2011-12-30'),
('Boyd', 200.0, '2011-12-29'),
('Boyd', 100.0, '2011-12-28'),
('Boyd', 100.0, '2011-12-27')
I myself prefer the Cross Apply & Top combination for simplicity.
UPDATE - I have fixed the date range calculation.
Also - if you need the query to be deterministic, either use the SELECT TOP N WITH TIES method or add a primary key or some kind of 'uniquifier' to the ORDER BY clause of the SELECT TOP query.
DECLARE #fd AS DATETIME;
DECLARE #ld AS DATETIME;
SET #fd = (dateadd(month, datediff(month, -1, getdate()) - 2, -1) + 1);
SET #ld = dateadd(month, datediff(month, -1, getdate()) - 1, -1);
WITH Customers AS (
SELECT CustomerName
FROM tempdb.dbo.CustomerTransaction
GROUP BY CustomerName
)
SELECT C.CustomerName,
SUM(Amount) AS Total
FROM Customers AS C
CROSS APPLY (
SELECT TOP (3) Amount
FROM tempdb.dbo.CustomerTransaction AS T
WHERE TransactionDate BETWEEN #fd AND #ld
AND T.CustomerName = C.CustomerName
ORDER BY TransactionDate DESC
) Q
GROUP BY C.CustomerName;
Results:
CustomerName Total
------------ ---------------------------------------
Boyd 400.00
John 300.00
I think the answer is still wrong, but very close. It still misses the last day.
The last day of the month at 12:00:00 AM is just the end of the previous day.
To include the full last day it should go until 11:59:59.999 PM
DECLARE #fd AS TIMEDATE;
DECLARE #ld AS TIMEDATE;
SET #fd = (dateadd(month, datediff(month, -1, getdate()) - 2, -1) + 1);
SET #ld = (dateadd(ms, -2, dateadd(month, datediff(month, -1, getdate()) - 1, -1) + 1));
PRINT #fd
PRINT #ld
This was a fun one!
http://sqlfiddle.com/#!3/ae6fd/20
with lastMonth as
(
select * from CustomerTransaction
where
TransactionDate >= DateAdd(m, -1, cast(cast(month(getdate()) as varchar) + '/1/' + cast(year(getdate()) as varchar) as date)) AND
TransactionDate < cast(cast(month(getdate()) as varchar) + '/1/' + cast(year(getdate()) as varchar) as date)
)
select
customerName,
sum(Amount) as total
from
lastMonth lm
where
not exists (
select 1 from lastMonth lm2
WHERE lm2.customerName = lm.customerName AND
exists (
select 1 from lastMonth lm3
WHERE lm3.customerName = lm2.customerName AND
exists (
select 1 from lastMonth lm4
WHERE lm4.customerName = lm3.customerName AND
lm4.TransactionDate > lm3.TransactionDate
) AND
lm3.TransactionDate > lm2.TransactionDate
) AND
lm2.TransactionDate > lm.TransactionDate
)
group by
customerName
Step by step why I did it this way:
LastMonth as a CTE so I would not have to filter the base dataset repetitively, both for performance and readability.
The last three values is accomplished via a chain of not exists + exists. These serve to filter out the data from the lastMonth so that only three records will be allowed to returned (done via what amounts to three joins).
declare #d datetime = '2012-01-31'
declare #CustomerTransaction table(CustomerName varchar(10), Amount money, TransactionDate datetime)
insert into #CustomerTransaction
(CustomerName, Amount, TransactionDate)
values ('John', 100.0, '2011-12-31'),
('John', 100.0, '2011-12-30'), ('John', 100.0, '2011-12-29'),
('John', 100.0, '2011-12-28'), ('Boyd', 100.0, '2011-12-30'),
('Boyd', 200.0, '2011-12-29'), ('Boyd', 100.0, '2011-12-28'),
('Boyd', 100.0, '2011-12-27')
;with cte as
(
select CustomerName, Amount, TransactionDate,
rn = row_number() over (partition by customername order by transactiondate desc)
from #CustomerTransaction--replace with: from Test.dbo.CustomerTransaction
where datediff(month, TransactionDate, #d) = 1
)
select CustomerName, Amount, TransactionDate
from cte where rn < 4