I have a SQL table that looks something like this:
Table definition:
CREATE TABLE TestSessions (Id INTEGER IDENTITY(1,1) PRIMARY KEY not null, TestUser varchar(50) not null, Department varchar(50) not null, Project varchar(50) not null, TestDateTime varchar(50) not null, Score varchar(50) not null, Pass varchar(50) not null);
I would like to group the table by TestUser and count each Pass (Passes and Fails separately) value for each TestUser so that it should look like:
SQL command: "SELECT TestUser, SUM(CASE WHEN [Score] = 'Pass' THEN 1 ELSE 0 END)[Pass], SUM( CASE WHEN[Score] = 'Fail' THEN 1 ELSE 0 END)[Fail] FROM TestSessions GROUP BY TestUser"
Returns:
How can I fix this?
Then I would like to calculate rate between Passes and Fails.
Is that possible in one statement?
You need a simple aggregation and a CASE expression:
SELECT Color,
SUM(CASE WHEN [BOOL] = 'TRUE' THEN 1 ELSE 0 END) [True],
SUM(CASE WHEN [BOOL] = 'FALSE' THEN 1 ELSE 0 END) [False]
FROM dbo.YourTable
GROUP BY Color;
This assumes that the datatype of BOOL is a varchar, if it's a bit, then you need to do:
SELECT Color,
SUM(CASE WHEN [BOOL] = 1 THEN 1 ELSE 0 END) [True],
SUM(CASE WHEN [BOOL] = 0 THEN 1 ELSE 0 END) [False]
FROM dbo.YourTable
GROUP BY Color;
First of all, your CASE should check on the column [Pass] instead of [Score], since [Score] never contains "Pass" or "Fail. Secondly, you could try and write a cte around it, like the following:
WITH cte AS (
SELECT TestUser
,Department
,CASE WHEN [Pass] = 'Pass' THEN 1 ELSE 0 END [Passed]
,CASE WHEN[Pass] = 'Fail' THEN 1 ELSE 0 END [Failed]
FROM TestSessions
)
SELECT TestUser
,Department
,sum([Passed]) [Pass]
,sum([Failed]) [Fail]
FROM cte
GROUP BY TestUser, Department
Related
In a table I have 914 rows in that I have a Column which contains "yes or no" values(Yes=193 No = 721 total 914).
In this I want to create a function to use in select statement How many Yes and No
I wrote a query
create function TSS(
#string as nvarchar(20)
)
returns int
begin
declare #result int
if (#string='NO')
select #result=sum(case Re_engaged when 'NO' then 1 else null end) from TVS_PRE
else if (#string='YES')
select #result=sum(case Re_engaged when 'YES' then 1 else null end) from TVS_PRE
return #result
end
select [dbo].[TSS]('Yes') as columns_with_Yes,[dbo].[TSS]('No') as columns_with_No from TVS_PRE
And I got the result is
Columns_with_Yes
Columns_with_No
1
193
2
193
3
193
upto...
...
914
193
but I required this
Columns_with_Yes
Columns_with_No
1
193
You should combine this into one query
SELECT
sum(case Re_engaged when 'NO' then 1 end) Columns_with_No,
sum(case Re_engaged when 'YES' then 1 end) Columns_with_Yes
FROM TVS_PRE;
If you really want this as a function, you can turn it into an inline Table valued Function
CREATE FUNCTION TSS()
RETURNS TABLE
AS RETURN (
SELECT
sum(case Re_engaged when 'NO' then 1 end) Columns_with_No,
sum(case Re_engaged when 'YES' then 1 end) Columns_with_Yes
FROM TVS_PRE
);
GO
I don't understand your need... but if you want only one row... then dont use "from":
select [dbo].[TSS]('Yes') as columns_with_Yes,[dbo].[TSS]('No') as columns_with_No
Use this syntax
SELECT DISTINCT COUNT(CASE WHEN Re_engaged='No' then 1 else NULL end) OVER () AS No_col,
COUNT(CASE WHEN Re_engaged='Yes' then 1 else NULL end) OVER () AS Yes_col
FROM TVS_PRE
I use dynamic properties to manage the characteristics of individuals. I have a table IndividusDynPropValues with 1 line for each individual and each property For example:
ID StartDate ValueString ValueFloat ValueDate Individus_ID IndividusDynProp
14 2018-09-10 Outside NULL NULL 3 Out Status
13 2018-08-15 Dead NULL NULL 1 Out Status
12 2018-08-02 Male NULL NULL 3 Sex
11 2018-07-28 #DBNULL# NULL NULL 1 Out Status
10 2018-07-25 Sold NULL NULL 1 Out Status
9 2018-06-07 Unk NULL NULL 3 Sex
8 2018-06-07 Adult NULL NULL 3 Status
7 2018-06-06 Femal NULL NULL 2 Sex
6 2018-06-06 Adult NULL NULL 2 Status
5 2018-06-03 Male NULL NULL 1 Sex
4 2018-06-03 Adult NULL NULL 1 Status
3 2018-05-23 Egg NULL NULL 3 Status
2 2018-05-23 Egg NULL NULL 2 Status
1 2018-05-21 Egg NULL NULL 1 Status
'#DBNULL#' means that the individual is on site again. I want to create a function that takes a date for the input parameter and returns a table with the ID of each individual who belong to the groups “Male living on site” at the date. An individual is part of the group if Sex = 'Male' AND (Out Status IS NULL OR Out Status = '#DBNULL#')
So in the example, if the date enter in the function is “2018-07-20” it returns:
Individus_ID
1
if the date is “2018-08-10” it returns:
Individus_ID
3
1
if the date is “2018-08-17” it returns:
Individus_ID
3
I’ve tried that:
CREATE FUNCTION fn_Groupe_Individus_Male_Vivant (#daDate datetime)
RETURNS #TGrMalesVivants TABLE (
TList_Individual [varchar] (50) NOT NULL
)
AS
BEGIN
DECLARE #TtempGrMalesVivants TABLE (TList_Individual [varchar] (50) NOT NULL)
DECLARE #TtempGrMales TABLE (TList_MIndividual int NOT NULL)
INSERT INTO #TtempGrMales
SELECT DISTINCT Individus_ID
FROM IndividusDynPropValues
DECLARE #selectedOutStatus VARCHAR(50),
#sBirdId int,
#tmpSex VARCHAR(3)
WHILE EXISTS (SELECT * FROM #TtempGrMales)
BEGIN
SELECT TOP 1 #sBirdId = TList_MIndividual FROM #TtempGrMales
SELECT #selectedOutStatus =IDPV.ValueString
FROM [dbo].[IndividusDynPropValues] AS IDPV
WHERE NOT EXISTS (SELECT *
FROM [dbo].[IndividusDynPropValues] AS IDPV2
WHERE IDPV.Individus_ID=IDPV2.Individus_ID
AND IDPV.IndividusDynProp_ID=IDPV2.IndividusDynProp_ID
AND IDPV2.StartDate>IDPV.StartDate AND CONVERT(smalldatetime,IDPV2.StartDate,120 )<=#daDate)
AND CONVERT(smalldatetime,IDPV.StartDate,120 )<=#daDate
AND IDPV.Individus_ID =#sBirdId
AND IDPV.IndividusDynProp_ID='Out Status'
SELECT #tmpSex= IDPV.ValueString
FROM [dbo].[IndividusDynPropValues] AS IDPV
INNER JOIN TSaisie AS TS ON TS.TSai_PK_ID=IDPV.Saisie_ID
INNER JOIN TProtocole AS TP ON TP.TPro_PK_ID=TS.TSai_FK_TPro_ID
WHERE NOT EXISTS (SELECT *
FROM [dbo].[IndividusDynPropValues] AS IDPV2
WHERE IDPV.Individus_ID=IDPV2.Individus_ID
AND IDPV.IndividusDynProp_ID=IDPV2.IndividusDynProp_ID
AND IDPV2.StartDate>IDPV.StartDate AND CONVERT(smalldatetime,IDPV2.StartDate,120 )<=#daDate)
AND CONVERT(smalldatetime,IDPV.StartDate,120 )<=#daDate
AND IDPV.Individus_ID =#sBirdId
AND IDPV.IndividusDynProp_ID='Sex'
AND TPro_Importance = (SELECT Max(TP2.Tpro_Importance)
FROM IndividusDynPropValues IDPV3
INNER JOIN TSaisie AS TS2 ON TS2.TSai_PK_ID=IDPV3.Saisie_ID
INNER JOIN TProtocole AS TP2 ON TP2.TPro_PK_ID=TS2.TSai_FK_TPro_ID
WHERE IDPV3.Individus_ID=#sBirdId
AND IDPV3.IndividusDynProp_ID=4
AND IDPV3.StartDate>=IDPV.StartDate
AND CONVERT(smalldatetime,IDPV3.StartDate,120 )<=#daDate)
IF #tmpSex='Male' AND (#selectedOutStatus IS NULL OR #selectedOutStatus='#DBNULL#')
BEGIN
INSERT INTO #TtempGrMalesVivants (TList_Individual)
VALUES (#sBirdId)
END
DELETE TOP (1) FROM #TtempGrMales
END
INSERT #TGrMalesVivants
SELECT *
FROM #TtempGrMalesVivants
RETURN
END
it works but it takes 1:55 with all the table (1859732 lines) so it’s too long
I think I understand what you are trying to do. Notice how I posted sample data? This is a good example for what you should do in the future.
Some conditional aggregation should work here. You need this aggregation because your data is denormalized into an EAV and you need to reassemble this into a normalized table. This works for the sample data and your desired output. You can uncomment the set #MyDate line to see the second value working.
if OBJECT_ID('tempdb..#IndividusDynPropValues') is not null
drop table #IndividusDynPropValues
create table #IndividusDynPropValues
(
ID int
, StartDate date
, ValueString varchar(50)
, ValueFloat float
, ValueDate date
, Individus_ID int
, IndividusDynProp varchar(50)
)
insert #IndividusDynPropValues values
(14, '2018-09-10', 'Outside', NULL, NULL, 3, 'Out Status')
, (13, '2018-08-15', 'Dead', NULL, NULL, 1, 'Out Status')
, (12, '2018-08-02', 'Male', NULL, NULL, 3, 'Sex')
, (11, '2018-07-28', '#DBNULL#', NULL, NULL, 1, 'Out Status')
, (10, '2018-07-25', 'Sold', NULL, NULL, 1, 'Out Status')
, (9 , '2018-06-07', 'Unk' , NULL, NULL, 3, 'Sex')
, (8 , '2018-06-07', 'Adult' , NULL, NULL, 3, 'Status')
, (7 , '2018-06-06', 'Femal' , NULL, NULL, 2, 'Sex')
, (6 , '2018-06-06', 'Adult' , NULL, NULL, 2, 'Status')
, (5 , '2018-06-03', 'Male' , NULL, NULL, 1, 'Sex')
, (4 , '2018-06-03', 'Adult' , NULL, NULL, 1, 'Status')
, (3 , '2018-05-23', 'Egg' , NULL, NULL, 3, 'Status')
, (2 , '2018-05-23', 'Egg' , NULL, NULL, 2, 'Status')
, (1 , '2018-05-21', 'Egg' , NULL, NULL, 1, 'Status')
declare #MyDate date = '20180720' --returns Individus_ID 1
--set #MyDate = '20180810' --returns Individus_ID 1, 3
;
with MySortedData as
(
select i.*
, RowNum = ROW_NUMBER()over(partition by i.Individus_ID, i.IndividusDynProp order by i.StartDate desc)
from #IndividusDynPropValues i
where i.StartDate <= #MyDate
)
select s.Individus_ID
, OutStatus = max(case when IndividusDynProp = 'Out Status' then ValueString end)
, Status = max(case when IndividusDynProp = 'Status' then ValueString end)
, Sex = max(case when IndividusDynProp = 'Sex' then ValueString end)
from MySortedData s
where s.RowNum = 1
group by s.Individus_ID
having isnull(max(case when IndividusDynProp = 'Out Status' then ValueString end), '#DBNULL#') = '#DBNULL#'
and max(case when IndividusDynProp = 'Sex' then ValueString end) = 'Male'
I am implementing MS SQL query in which I need to check all conditions and get a merged result in one field(OupputField) as a string.
Table: Customer
Id Customer Type Active
------------------------
1 John 1 1
2 Bob 2 0
3 TOM 0 1
I trying to get OupputField values by checking ALL different cases. but it checks for one true condition and returns the result. I need to check all the cases and need to return combine result as OutputField
Expected Output:
Id Customer Type Active OutputField
--------------------------------------
1 John 1 1 Type1, Active
2 Bob 2 0 Type2
3 TOM 0 1 Active
MS SQL Query:
select
Id,
Customer,
TypeId
Active
COALESCE
(
CASE
WHEN TypeId = 1
THEN 'Type1'
END,
CASE
WHEN Active = 1
THEN 'Active'
END
)
from Customer
Am I doing something wrong? Please help me.
Thank you in advance.
select case when type = 0 and active = 0 then ''
when type = 0 and active = 1 then 'Active'
when type > 0 and active = 0 then 'Type' + cast(type as varchar)
when type > 0 and active = 1 then 'Type' + cast(type as varchar) + ', Active'
end as result
from your_table
CREATE TABLE #tmp(Id int, Customer varchar(20), TypeId int, Active int)
INSERT INTO #tmp (Id,Customer,TypeId,Active) VALUES(1,'John',1,1)
INSERT INTO #tmp (Id,Customer,TypeId,Active) VALUES(2,'Bob',2,0)
INSERT INTO #tmp (Id,Customer,TypeId,Active) VALUES(3,'TOM',0,1)
INSERT INTO #tmp (Id,Customer,TypeId,Active) VALUES(4,'Jim',0,0)
select Id, Customer,
TypeId
Active,
CASE WHEN TypeID >0 THEN 'TypeId' + CAST(TypeID as varchar(3)) ELSE '' END +
CASE WHEN TypeID > 0 AND Active > 0 THEN ', ' ELSE '' END +
CASE WHEN Active > 0 THEN 'Active' ELSE '' END OutputField
from #tmp
I have a table that looks like this:
CREATE TABLE dbo.Mails (
ID int IDENTITY(1, 1) NOT NULL,
Reference nvarchar(20) COLLATE Latin1_General_CI_AS NULL,
Email nvarchar(70) NOT NULL,
ETS datetime NULL, --Estimated Time of Shipping
ATS datetime NULL, --Actual Time of Shipping
ReadOn datetime NULL,
Unsubscribed datetime NULL,
Bounced datetime NULL,
BouncedReason nvarchar(30) COLLATE Latin1_General_CI_AS NULL,
Active bit DEFAULT 1 NULL
)
I need to show info on a chart, and I need to group by Date.
therefore if I want to group details by ReadOn field for a certain campaign I build the following query
Select
CAST(readOn as date) [date],
COUNT(*) [read]
FROM Mails m
WHERE m.Reference=#Reference
GROUP BY CAST(readOn as date)
ORDER BY CAST(readOn as date) ASC
and I get something like this:
sDate read
NULL 360
2016-05-05 67
2016-05-06 123
2016-05-07 84
2016-05-08 62
2016-05-09 89
2016-05-10 17
2016-05-11 12
2016-05-12 8
2016-05-13 4
2016-05-14 4
But I would like to extract, in the same query, not only ReadOn field, but also other fields like ETS, ATS, Unsubscribed/Read & Unread and Bounced
and get something like this
sDate read ETS ATS Bounced Unsub./Read Unsub/Unread
NULL 360
2016-05-05 67 830 570 27 7 3
2016-05-06 123 0 260 4 9 5
2016-05-07 84 0 0 0 2 2
2016-05-08 62 0 0 0 2 4
2016-05-09 89 0 0 0 7 1
2016-05-10 17 0 0 0 5 6
2016-05-11 12 0 0 0 8 2
2016-05-12 8 0 0 0 1 3
2016-05-13 4 0 0 0 0 2
2016-05-14 4 0 0 0 0 2
Is there an easier way than building 6 different queries?
can at least indicate the path to follow?
Thanks
Joe
You can do it with some pre-processing and a PIVOT. In this example, I've put the query into a stored procedure, so that it is contained and easy to test. I'm doing the pre-processing in a CTE, to keep the main query tidy.
First, create the table and populate it.
CREATE TABLE dbo.Mails
(
ID int IDENTITY(1, 1) NOT NULL,
Reference nvarchar(20) COLLATE Latin1_General_CI_AS NULL,
ETS datetime NULL, --Estimated Time of Shipping
ATS datetime NULL, --Actual Time of Shipping
ReadOn datetime NULL,
Unsubscribed datetime NULL,
Bounced bit DEFAULT 0 NULL,
BouncedReason nvarchar(30) COLLATE Latin1_General_CI_AS NULL,
Active bit DEFAULT 1 NULL
);
GO
INSERT INTO dbo.Mails (Reference, ETS, ATS, ReadOn, Unsubscribed, Bounced)
VALUES
(N'ABC', '2015-05-05', '2015-05-05', '2015-05-05', NULL, 0),
(N'ABC', '2015-05-06', '2015-05-07', '2015-05-08', NULL, 0),
(N'ABC', '2015-05-05', '2015-05-05', '2015-05-07', NULL, 0),
(N'ABC', '2015-05-07', '2015-05-08', '2015-05-09', NULL, 0),
(N'ABC', '2015-05-06', '2015-05-07', '2015-05-09', '2015-05-09', 0),
(N'ABC', '2015-05-06', '2015-05-07', NULL, '2015-05-08', 0);
Then create a stored procedure with a parameter #Reference. I'm using a CTE to create a two column row set, with Date and Type as the columns. Then, in the main SELECT statement it's being pivoted to give the result you want.
The row set produced by the CTE looks like this.
Note: I haven't included the Bounced column, because I'm not clear what the requirement is for that; it's not a date column. However you should be able to extend this example quite easily.
CREATE PROCEDURE dbo.up_ReportMails
(
#Reference nvarchar(20)
)
AS
WITH cte AS
(
SELECT CAST(ReadOn AS date) AS 'Date', 'R' AS 'Type'
FROM dbo.Mails
WHERE Reference = #Reference AND ReadOn IS NOT NULL
UNION ALL
SELECT CAST(ETS AS date), 'E'
FROM dbo.Mails
WHERE Reference = #Reference AND ETS IS NOT NULL
UNION ALL
SELECT CAST(ATS AS date), 'A'
FROM dbo.Mails
WHERE Reference = #Reference AND ATS IS NOT NULL
UNION ALL
SELECT CAST(Unsubscribed AS date), 'U'
FROM dbo.Mails
WHERE Reference = #Reference AND UNSUBSCRIBED IS NOT NULL AND ReadOn IS NOT NULL
UNION ALL
SELECT CAST(Unsubscribed AS date), 'V'
FROM dbo.Mails
WHERE Reference = #Reference AND UNSUBSCRIBED IS NOT NULL AND ReadOn IS NULL
)
SELECT [Date], [R] AS 'Read', [E] AS 'ETS', [A] AS 'ATS', [U] AS 'Unsub/Read', [V] As 'Unsub/Unread'
FROM
(SELECT [Date], [Type]
FROM cte) AS C
PIVOT
(
COUNT([Type])
FOR [Type] IN ([R], [E], [A], [U], [V])
) AS PivotTable
ORDER BY [Date];
Then we can test it.
EXEC dbo.up_ReportMails #Reference=N'ABC';
I've tested this code and it works. Assuming you have a calendar table (if you don't have one, you can Google it and make one in about 10 minutes - they're very simple and will save you loads of time):
DECLARE #StartDate date = '01/01/2016'
DECLARE #EndDate date = '05/06/2016'
SELECT C.BaseDate,
ISNULL(SUM(CASE WHEN C.BaseDate = CAST(M.ETS AS DATE) THEN 1 END), 0) AS [ETS],
ISNULL(SUM(CASE WHEN C.BaseDate = CAST(M.ATS AS DATE) THEN 1 END), 0) AS [ATS],
ISNULL(SUM(CASE WHEN C.BaseDate = CAST(M.ReadOn AS DATE) THEN 1 END), 0) AS [Read On],
ISNULL(SUM(CASE WHEN C.BaseDate = CAST(M.Unsubscribed AS DATE) THEN 1 END), 0) AS [Unsubscribed],
ISNULL(SUM(CASE WHEN C.BaseDate = CAST(M.Bounced AS DATE) THEN 1 END), 0) AS [Bounced]
FROM Calendar C
LEFT OUTER JOIN Mails M
ON C.BaseDate = CAST(M.ETS AS DATE)
OR C.BaseDate = CAST(M.ATS AS DATE)
OR C.BaseDate = CAST(M.ReadOn AS DATE)
OR C.BaseDate = CAST(M.Unsubscribed AS DATE)
OR C.BaseDate = CAST(M.Bounced AS DATE)
WHERE C.BaseDate BETWEEN #StartDate AND #EndDate
GROUP BY C.BaseDate
Basically what you're doing is selecting every date from the calendar table within your date range, and then joining it to your mails table if ANY of the datetimes match that date. The purpose of the left join is so that dates on which nothing occurs are still returned in your result set. They will all be zeros, but it's better for consistency and in case someone wants to calculate averages from your report.
Once you have all the dates - and all of the records that have a matching datetime, you just need to count how many, for each date, have a matching ETS, how many have a matching ATS, so on and so forth. Last, you group by the calendar date and you're all done.
I need to create a query that will sum the number of True(1) and False(0) into two separate columns from one bit field.
I'm joining 3 tables and need it to be something like:
Attribute | Class | Pass | Fail
I will be grouping on Attribute and Class.
Something like this:
SUM(CASE WHEN ColumnName = 1 THEN 1 ELSE 0 END) AS Pass,
SUM(CASE WHEN ColumnName = 0 THEN 1 ELSE 0 END) AS Fail
This works (at least in SQL 2008)
SELECT SUM(Passed + 0) PASS , SUM(1 - Passed) FAIL
I am adding 0 to Passed in the first sum as a short hand way of converting from bit to int since you can't sum bits directly.
try:
declare #table table (columnName bit)
insert into #table values (1)
insert into #table values (1)
insert into #table values (1)
insert into #table values (1)
insert into #table values (1)
insert into #table values (0)
insert into #table values (0)
insert into #table values (0)
insert into #table values (0)
SELECT
SUM(CASE WHEN ColumnName = 1 THEN 1 ELSE 0 END) AS True1
, SUM(CASE WHEN ColumnName = 0 THEN 1 ELSE 0 END ) AS False0
from #Table
OUTPUT:
True1 False0
----------- -----------
5 4
(1 row(s) affected)
SELECT
Attribute,
Class,
SUM(CASE BitField WHEN 1 THEN 1 ELSE 0 END) AS [Pass],
SUM(CASE BitField WHEN 0 THEN 1 ELSE 0 END) AS [Fail]
FROM
Table
GROUP BY
Attribute,
Class
Another option would be
SELECT Attribute, Class
COUNT(CASE WHEN ColumnName = 1 THEN 1 END) Pass,
COUNT(CASE WHEN ColumnName = 0 THEN 1 END) Fail FROM YourTable
GROUP BY Attribute, Class
there is even one more option:
SELECT
Attribute,
Class,
COUNT(BoolColumnName = 1 or NULL) Pass,
COUNT(BoolColumnName = 0 or NULL) Fail
FROM Table
GROUP BY Attribute, Class