This question already has answers here:
How can I get multiple counts with one SQL query?
(12 answers)
Closed 4 years ago.
I have a set of queries that represent the data from different sysdates (from the last 5, 7 and 30 days).
My doubt is how to express in a query this results in this matter:
STATE | 5 DAYS | 7 DAYS | 30 DAYS
---------------------------------
INIT | 1 | 1 | 2
---------------------------------
SECN | 2 | 2 | 2
NOTE: This is from a single table with different sysdates in consideration
NOTE2: An query example is this
select
CASE WHEN STATUS = 'INI' then 'Initial'
WHEN STATUS = 'SECN' the 'Second'
END 'Status', count(*)
from db.FilesTable
where 1=1
and DAT_Files >= DATEADD(day,-5,GETDATE())
Use conditional aggregation to count records only when a particular condition occurs. The CASE will be computed before the aggregation occurs, so you can put any expression on any column.
select
State = T.Status,
[5 Days] = COUNT(CASE WHEN T.DAT_Files >= DATEADD(day, -5, GETDATE()) THEN 1 END),
[7 Days] = COUNT(CASE WHEN T.DAT_Files >= DATEADD(day, -7, GETDATE()) THEN 1 END),
[30 Days] = COUNT(1)
from
db.FilesTable AS T
where
T.Status IN ('INI', 'SECN') AND
DAT_Files >= DATEADD(day, -30, GETDATE()) -- Biggest period filter here
GROUP BY
T.Status
I think you could write a query of this type:
SELECT State,
SUM (CASE WHEN ColA < 6 THEN 1 ELSE 0 END) AS '5 Days',
SUM (CASE WHEN ColA IN (6, 7) THEN 1 ELSE 0 END) AS '7 Days',
SUM (CASE WHEN ColA > 7 AND ColA < 31 THEN 1 ELSE 0 END) AS '30 Days'
FROM TableA
Obviously you might want to adjust something, but this gives some direction.
Related
I have a matrix visual that has 3 data fields (MTD, YTD, ITD) in the columns section. When I drill down to each level they show exactly as they should (Pictures below for each level of drill down).
I do not wish to drill down to see each column. I want all columns displayed at once. I have tried things like removing stepped layout from row headers format and expanding all down a hierarchy. I bring in the fields (MTD, YTD, ITD) using the code below. I'm at my wits end, any ideas?
,MAX(CASE WHEN EffectiveDate >= DATEFROMPARTS(YEAR(GETDATE()), MONTH(DATEADD(MONTH, -1, GETDATE())), 1) THEN ''MTD'' ELSE ''Not MTD'' END) OVER (PARTITION BY EffectiveDate) AS MTD
,MAX(CASE WHEN EffectiveDate >= DATEFROMPARTS(YEAR(GETDATE()), 1, 1) THEN ''YTD'' ELSE ''Not YTD'' END) OVER (PARTITION BY EffectiveDate) AS YTD
,MAX(CASE WHEN EffectiveDate >= DATEFROMPARTS(2013, 1, 1) THEN ''ITD'' ELSE ''Not ITD'' END) OVER (PARTITION BY EffectiveDate) AS ITD
EDIT:
Sample data
Building
Rentpaid
Complaint
BuildingExpense
Date
A
2
blah
4
1/1/2021
A
3
blah
6
4/1/2021
A
1
NULL
5
3/2/2022
B
2
NULL
4
7/12/2022
B
4
blah
NULL
10/2/2022
Desired Output (Power bi Matrix)
Building
MTD
YTD
ITD
A
SUMRentTransactions
0
1
6
ComplaintCount
0
0
2
BuildingExpenseCount
0
1
3
B
SUMRentTransactions
4
6
6
ComplaintCount
1
1
1
BuildingExpenseCount
0
1
1
.
So, instead of max, you want case sum coalesce.
so something like
sum(coalesce(case when date > dateadd(day, -1, getdate()) rentPaid,0 end,0)) as MTD
Repeat that for all the other cases.
I have a table that shows only the 'captured' data. For example in the below exhibit, the emp_no 17 has 2 records - for November and February (for a specified 6 month period, from July 2017). It does not have data for the other 4 months (within the 6-month date range, from previous 6 months to current date).
How can I populate these missing months (Sept, Oct, Dec) with default values for num_differences of 0 for the missing months? (for example, in this case, I want emp_no 17 to have the below (I can ignore 2018 data - only require data up to Dec 2017):
I have the script below:
declare #YMN date;
set #YMN = '20171201';
DECLARE #Emp TABLE (
[date] date,
[emp_no] int,
[num_differences] int
);
INSERT INTO #Emp VALUES
('2017-09-14', 17, 1), ('2017-12-01', 17, 1),('2017-12-18', 17, 1),('2017-12-21', 17, 1),
('2017-09-27', 17, 1), ('2017-12-04', 17, 1);
-------------------------------------------------------------------------------------------get missing dates---------------------------------------------------------------------------
;WITH cte_Emp_No AS (
SELECT DISTINCT [emp_no]
FROM #Emp
),
cte_dates AS (
SELECT [emp_no], DATEADD(month, -6, DATEADD(dd, -(DAY(dateadd(month, 1, #YMN)) - 1), dateadd(month, 1, #YMN))) AS [date]
FROM cte_Emp_No
UNION ALL
SELECT [emp_no], DATEADD(month, 1, [date]) AS [date]
FROM cte_dates
WHERE [date] < dateadd(month, 0, #YMN)
)
SELECT DISTINCT ISNULL(e.emp_no, c.emp_no) emp_no, ISNULL(e.date, c.date) date, ISNULL(e.num_differences, 0) num_differences
into ##new_table
FROM #Emp AS e
RIGHT JOIN cte_dates AS c ON YEAR(c.date) = YEAR(e.date) AND MONTH(c.date) = MONTH(e.date)
-----------------------------------------------------------------------------------------------MAIN CTE------------------------------------------------------------------------------
;with cte_RawScore as
(
select emp_no
, date YMN
,sum(case when datediff(month, convert(datetime, #YMN, 112), date) = 0 then num_differences else 0 end) as thismonth
,sum(case when datediff(month, convert(datetime, #YMN, 112), date) between -2 and 0 then num_differences else 0 end) as last3month
,sum(case when datediff(month, convert(datetime, #YMN, 112), date) between -5 and 0 then num_differences else 0 end) as last6month
from ##new_table d
group by emp_no, date
)
select
emp_no
,YMN
,case when last6month = 0 then 5
when last3month = 0 then 4
when thismonth = 0 then 3
when thismonth <= 3 then 2
else 1 end RawScore
from cte_RawScore
ORDER BY day(YMN) desc
drop table ##new_table
I want this the scoring only to be applicable for 6 months from and after July 2017. i.e. the #YMN is a variable that stores the year month number; and the score, according to the above rule applies to the 6 months from 201707.
So 201707 is 1 month,
201708 is 2 months, etc, up to 201712
I wish to have a list of employees with their associated scores, based on the rules mentioned below .
That’s, :
A score of 5 if 0 differences in 6 consecutive months ( from July to December) ;
A score of 4 if 0 differences in 3 consecutive months (from July to December);
A score of 3 if 0 differences for 1 month ( from July to December);
A score of 2 if 1 to 3 differences for 1 month (from July to December);
A score of 1 if 4 or more differences in 1 month (from July to December).
I get the number of differences from a table, but some employees do not appear for certain months; hence I want to give them a difference of 0 if they do not appear for that particular month.
Please assist.
I think I understand what you're getting at. Let me give you a simplified example. You need a table full of dates to join to. In data warehousing we use a Date dimension which has attributes about every date.
For your example your date dimension table could just have Month names or numbers:
1
2
...
12
Let's call this table Months.
Then you would do something like this, to count a zero for months with no data. Here I'm using what's called a Common Table Expression or CTE (the part with the WITH) in place of a table, since I'm not concerned with creating a permanent table right now.
WITH Months AS (
SELECT 1 AS MonthNumber UNION
SELECT 2 UNION
SELECT 3 UNION
SELECT 4 UNION
SELECT 5 UNION
SELECT 6 UNION
SELECT 7 UNION
SELECT 8 UNION
SELECT 9 UNION
SELECT 10 UNION
SELECT 11 UNION
SELECT 12
)
SELECT M.MonthNumber, COUNT(*)
FROM Months as M
LEFT JOIN MyData as D
ON MONTH(D.SomeDateValue) = M.MonthNumber
GROUP BY M.MonthNumber
This will guarantee every month appears with a count, perhaps of zero.
I have a table with different products that gets registered constantly into different locations. I want to know for how long this product existed since the first scan it had and where it's current location is.
here is an example:
CREATE TABLE Products
(
ProductNR varchar (14),
Location int,
Scanned Datetime
);
Insert Into Products(ProductNR, Location, Scanned)
Values('1111', 1, '2017-03-15 09:30'),
('1111', 2, '2017-03-16 11:35'),
('1111', 3, '2017-03-21 12:37'),
('2222', 1, '2017-03-21 09:50'),
('2222', 5, '2017-03-21 12:58');
Expected output:
-----------------------------------------------------
| ID | Current Location | Age
-----------------------------------------------------
| 1111 | 3 | > 5 days
| 2222 | 5 | < 5 days
-----------------------------------------------------
My current query
select ProductNR,
Location,
Scanned,
case When Scanned >= DATEADD(day, -5, GETDATE()) Then 'Less than 5 days old'
When scanned <= DATEADD(day, -5, GETDATE()) Then 'More than 5 days old' else '0' end AS Age
From Products
I think this Query is completely wrong and it's also showing every record, I spent ages trying to figur out how to write this correctly. I hope someone can help
Try this query:
select p.ProductNR, p.Location, p.Scanned
,case
when p.FirstScanned >= dateadd(day, -5, getdate()) then 'Less than 5 days old'
when p.FirstScanned <= dateadd(day, -5, getdate()) then 'More than 5 days old'
else '0'
end as Age
from
(
select p.ProductNR
,p.Location
,p.Scanned
,min(p.Scanned) over (partition by p.ProductNR) as FirstScanned
,max(p.Scanned) over (partition by p.ProductNR) as LastScanned
from Products p
) p
where p.LastScanned = p.Scanned
Can you try this?
SELECT PRODUCTNR, CASE WHEN DATEDIFF(dd,MIN_DATE, SCANNED) > 5 THEN 'More than 5 days old' eLSE 'Less than 5 days old' END AS LAST_SCANN
FROM (
SELECT PRODUCTNR, LOCATION, SCANNED , ROW_NUMBER() OVER(PARTITION BY PRODUCTNR ORDER BY SCANNED DESC) AS RN
, MIN(SCANNED) OVER(PARTITION BY PRODUCTNR ) AS MIN_DATE
FROM PRODUCTS
) X WHERE RN=1;
I am fetching COUNT from 3 different table based on some conditions but to group them on time interval. (Like: 1 hour, 30 minutes.)
I need the following output:
Date Interval Success Un-Success Closed CLInotFound
2/20/2016 01:01 – 02:00 5 3 2 13
2/20/2016 02:01 – 03:00 14 9 23 5
2/20/2016 03:01 – 04:00 8 67 89 345
2/20/2016 04:01 – 05:00 2 23 92 12
2/20/2016 05:01 – 06:00 44 55 78 98
2/20/2016 06:01 – 07:00 12 87 56 445
I am able to calculate them separately but when I am trying to combine the result gets different.
Query 1 For Success & Un-Success:
SELECT CONVERT(VARCHAR(5), A.InsertionDate ,108) AS 'Interval',
COUNT(CASE WHEN A.call_result = 0 then 1 ELSE NULL END) AS 'Success',
COUNT(CASE WHEN A.call_result = 1 then 1 ELSE NULL END) AS 'Un-Success'
from dbo.AutoRectifier A
WHERE CONVERT(DateTime,A.InsertionDate,101) BETWEEN '2016-02-19 02:10:35.000' AND '2016-02-19 07:15:35.000'
GROUP BY A.InsertionDate;
Query 2 For Closed:
SELECT CONVERT(VARCHAR(5), C.DateAdded ,108) AS 'Interval',
COUNT(*) AS 'Closed' FROM dbo.ChangeTicketState C
WHERE C.SourceFlag = 'S-CNR' AND C.RET LIKE '%CLOSE%'
AND C.DateAdded BETWEEN '2016-02-19 02:10:35.000' AND '2016-02-19 07:15:35.000'
GROUP BY C.DateAdded;
Query 3 For CLI Not Found:
SELECT CONVERT(VARCHAR(5), T.DateAdded ,108) AS 'Interval',
COUNT(*) 'CLI Not Found' FROM dbo.TICKET_INFO T
WHERE T.CONTACT_NUMBER = '' AND T.DateAdded BETWEEN '2016-02-19 02:10:35.000' AND '2016-02-19 07:15:35.000'
GROUP BY T.DateAdded;
You have got several problems to solve in you question.
You have to produce a union result set from Query1, Query2, Query3 to group it. You can use UNION ALL for it but all 3 queries must have similar column list for it. So, add
0 as Closed, 0 as CLInotFound
to select-list of the Query1,
add
0 as Success, 0 as Un-Success, 0 as CLInotFound
to select-list of the Query2 and add
0 as Success, 0 as Un-Success, 0 as Closed
to Query3
Then you can write
select * from Query1
union all
select * from Query2
union all
select * from Query3
Don't convert date to varchar at Query1, Query2, Query3. Better return datetime from query to use it for grouping after union. So, query 1 will look like
SELECT A.InsertionDate AS Date, ...
Query2 -
SELECT C.DateAdded AS Date, ...
etc.
Then you can group results on per-hour basis, for instance using GROUP BY SUBSTRING(CONVERT(VARCHAR(20), Date ,120), 1, 13)
So, the result will look like
SELECT SUBSTRING(CONVERT(VARCHAR(20), Date ,120), 1, 13) as Interval,
sum(Success) as
sum(Un-Success) as,
sum(Closed) as,
sum(CLInotFound) as
from (
select * from Query1
union all
select * from Query2
union all
select * from Query3
) q
GROUP BY SUBSTRING(CONVERT(VARCHAR(20), Date ,120), 1, 13)
Its result have slightly different format of Date and Interval field, but shows the idea.
You can use GROUP BY DATEPART(yy, Date), DATEPART(mm, Date), DATEPART(dd, Date), DATEPART(hh, Date) instead of GROUP BY SUBSTRING(CONVERT(VARCHAR(20), Date ,120), 1, 13) and format if as you wish.
Also result set does not contain intervals that not present at original data.
You can add Query4, containing all intervals required and zeros at all fields to fix it.
I have a query in MS SQL Server asking for name and some date-related information, depending on two dates, a start- and an enddate.
The problem is, I´m not always getting the same performance. Whenever I request something between the dates;
2010-07-01 00:00:00.000 and
2011-07-21 23:59:59.999
the performance is excellent. I get my result within mseconds. When I request something between these dates, for example,
2011-07-01 00:00:00.000 and
2011-07-21 23:59:59.999
the performance is.. less than good, taking between 20-28 seconds for each query. Do note how the dates giving good performance is more than a year between, while the latter is 20 days.
Is there any particular reason (maybe related to how DATETIME work) for this?
EDIT: The query,
SELECT ENAME,
SUM(CASE DATE WHEN 0 THEN 1 ELSE 0 END) AS U2,
SUM(CASE DATE WHEN 1 THEN 1 ELSE 0 END) AS B_2_4,
SUM(CASE DATE WHEN 2 THEN 1 ELSE 0 END) AS B_4_8,
SUM(CASE DATE WHEN 3 THEN 1 ELSE 0 END) AS B_8_16,
SUM(CASE DATE WHEN 4 THEN 1 ELSE 0 END) AS B_16_24,
SUM(CASE DATE WHEN 5 THEN 1 ELSE 0 END) AS B_24_48,
SUM(CASE DATE WHEN 6 THEN 1 ELSE 0 END) AS O_48,
SUM(CASE DATE WHEN 7 THEN 1 ELSE 0 END) AS status,
AVG(AVG) AS AVG,
SUM(DATE) AS TOTAL
FROM
(SELECT ENAME,
(CASE
WHEN status = 'Öppet' THEN 7
WHEN DATE < 48 THEN
(CASE WHEN DATE BETWEEN 0 AND 2 THEN 0
WHEN DATE BETWEEN 2 AND 4 THEN 1
WHEN DATE BETWEEN 4 AND 8 THEN 2
WHEN DATE BETWEEN 8 AND 16 THEN 3
WHEN DATE BETWEEN 16 AND 24 THEN 4
WHEN DATE BETWEEN 24 AND 48 THEN 5
ELSE - 1 END)
ELSE 6 END) AS DATE,
DATE AS AVG
FROM
(SELECT DATEDIFF(HOUR, cases.date, status.date) AS DATE,
extern.name AS ENAME,
status.status
FROM
cases INNER JOIN
status ON cases.id = status.caseid
AND status.date =
(SELECT MAX(date) AS Expr1
FROM status AS status_1
WHERE (caseid = cases.id)
GROUP BY caseid) INNER JOIN
extern ON cases.owner = extern.id
WHERE (cases.org = 'Expert')
AND (cases.date BETWEEN '2009-01-15 09:48:25.633'
AND '2011-07-21 09:48:25.633'))
AS derivedtbl_1)
AS derivedtbl_2
GROUP BY ENAME
ORDER BY ENAME
(parts of) The tables:
Extern
-ID (->cases.owner)
-name
Cases
-Owner (->Extern.id)
-id (->status.caseid)
-date (case created at this date)
Status
-caseid (->cases.id)
-Status
-Date (can be multiple, MAX(status.date) gives us date when
status was last changed)
I would have thought a statistics issue.
When you are only selecting the most recent dates these may be unrepresented in the statistics yet as the threshold has not yet been reached that would trigger auto updating.
See this blog post for an example.