I have many records of products that have status.
I wanted to count the records by status and place these results as columns. I tried with case, but I have duplicated rows that differ on the status.
Received StandBy Stocked Pending
Product-1 2 NULL NULL NULL
Product-2 NULL 25 NULL NULL
Product-1 NULL 5 NULL NULL
I would like something like this
Received StandBy Stocked Pending
Product-1 2 5 NULL NULL
Product-2 NULL 25 NULL NULL
This is the query that I try to do without success:
SELECT
--COALESCE(StatusID, 0) AS StatusID, --=1,2
ProductID,
ProductNumber,
DATEPART(hour,p.ArrivalDate) as ArrivalHour,
DATEPART(minute,p.ArrivalDate) as ArrivalMinute,
ProductWarehouseID,
SUM(CASE WHEN StatusID = 1 THEN 1 END) AS Received,
SUM(CASE WHEN StatusID = 2 THEN 1 END) AS StandBy,
SUM(CASE WHEN StatusID = 3 THEN 1 END) AS Stocked,
SUM(CASE WHEN StatusID = NULL THEN 1 END) AS Pending
FROM Product AS p
GROUP BY
ProductID,
ProductNumber,
DATEPART(hour, p.ArrivalDate),
DATEPART(minute, p.ArrivalDate),
ProductWarehouseID
I believe you have given partial query output to simply your solution. If my understanding is correct, you may need to use an aggregate operator over your result. An outline is here:
;WITH Temp AS
(
--Keep your current query here
)
SELECT ProductID, MAX(Received), MAX(StandBy), MAX(Stocked), MAX(Pending)
FROM Temp
GROUP BY ProductID --and any other grouping columns
I think your key stumbling point is null + 1 = null when concat null is at its default settings in MsSql servers after Sql 2000. You may be able to solve your issue by changing the settings of the null concat settings. Or include the "else 0" syntax that others have posted before this post.
Related
I'm creating a report using SSRS, and I have a bunch of departments and I need to count the total of their running statuses. This is the result from my table
Please note, the UNKNOWN department shows NULL inside the table. i hard coded to 'UNKNOWN' --ISNULL(department,'UNKNOWN')
And i have tested the table has NULL record and I can count those NULL record COUNT(*)
However, it seems like SSRS does not count NULL values.
the SSRS expression i had its =COUNT(Fields!ID.Value)
I need UNKNOWN rows count just as other department
How do I fix this?
I think your problem comes from how the query was written. This is a guess (you didn't provide the query) but I expect you did something like this:
/* Start Demo Data */
DECLARE #Departments TABLE (DepartmentID INT IDENTITY, Name NVARCHAR(50));
INSERT INTO #Departments (Name) VALUES
('Architect'),('Business Intelligence Analyst'),('Data Analyst'),
('Database'),('Information Technology'),('Technical Analyst');
DECLARE #Tickets TABLE (TicketID INT IDENTITY, CreateDateUTC DATETIME DEFAULT GETUTCDATE(), DepartmentID INT, Status NVARCHAR(50));
INSERT INTO #Tickets (DepartmentID, Status) VALUES
(1, 'Completed'),
(2, 'Completed'),(2, 'Completed'),
(3, 'Completed'),(3, 'Completed'),(3, 'Completed'),(3, 'Completed'),(3, 'Completed'),
(3, 'Failure'),(3, 'Failure'),(3, 'Running'),(3, 'Running'),(3, 'Failure'),
(4, 'Completed'),
(5, 'Completed'),(5, 'Completed'),(5, 'Failure'),(5, 'Running'),(5, 'Completed'),
(6, 'Completed'),
(7, 'Failure'),(7, 'Completed');
/* End Demo Data */
SELECT COALESCE(d.Name,'Unknown') AS Department,
COUNT(CASE WHEN t.Status = 'Completed' THEN 1 END) AS Completed,
COUNT(CASE WHEN t.Status = 'Failure' THEN 1 END ) AS Failure,
COUNT(CASE WHEN t.Status = 'Running' THEN 1 END ) AS Running,
COUNT(t.Status) AS Total
FROM #Departments d
INNER JOIN #Tickets t
ON t.DepartmentID = d.DepartmentID
GROUP BY d.Name
ORDER BY Department
Department Completed Failure Running Total
-----------------------------------------------------------------
Architect 1 0 0 1
Business Intelligence Analyst 2 0 0 2
Data Analyst 5 3 2 10
Database 1 0 0 1
Information Technology 3 1 1 5
Technical Analyst 1 0 0 1
This will find all the tickets with a matching department ID in the tickets table, but it will not return any tickets which have a non-matching value in the departmentID column, a NULL for example.
If you change your approach to something like:
SELECT COALESCE(d.Name,'Unknown') AS Department,
COUNT(CASE WHEN t.Status = 'Completed' THEN 1 END) AS Completed,
COUNT(CASE WHEN t.Status = 'Failure' THEN 1 END ) AS Failure,
COUNT(CASE WHEN t.Status = 'Running' THEN 1 END ) AS Running,
COUNT(t.Status) AS Total
FROM #Tickets t
LEFT OUTER JOIN #Departments d
ON t.DepartmentID = d.DepartmentID
GROUP BY d.Name
ORDER BY Department
You're now asking for all the tickets, and joining that to the departments with a LEFT OUTER JOIN which allows non-matching rows from Tickets to be returned as well. When there is a non-matching (including NULL) value in the departmentID column, it's still part of the result set.
Department Completed Failure Running Total
-----------------------------------------------------------------
Architect 1 0 0 1
Business Intelligence Analyst 2 0 0 2
Data Analyst 5 3 2 10
Database 1 0 0 1
Information Technology 3 1 1 5
Technical Analyst 1 0 0 1
Unknown 1 1 0 2
I am attempting to pivot a database so that only certain rows become columns. Below is what my table looks like:
ID QType CharV NumV
1 AccNum 10
1 EmpNam John Inc 0
1 UW Josh 0
2 AccNum 11
2 EmpNam CBS 0
2 UW Dan 0
I would like the table to look like this:
ID AccNum EmpNam
1 10 John Inc
2 11 CBS
I have two main problems I am trying to account for.
1st: the value that I am trying to get isn't always in the same column. So while AccNum is always in the NumV column, EmpName is always in the CharV column.
2nd: I need to find a way to ignore data that I don't want. In this example it would be the row with UW in the QType column.
Below is the code that I have:
SELECT *
FROM testTable
Pivot(
MAX(NumV)
FOR[QType]
In ([AccNum],[TheValue])
)p
But it's giving me the below result:
ID CharV AccNum TheValue
1 10 NULL
2 11 NULL
2 CBS NULL NULL
2 Dan NULL NULL
1 John IncNULL NULL
1 Josh NULL NULL
In this case grouping with conditional aggregation should work. Try something like:
SELECT ID
, MAX(CASE WHEN QType = 'AccNum' THEN NumV END) AS AccNum
, MAX(CASE WHEN QType = 'EmpNam' THEN CharV END) AS EmpNam
FROM testTable
GROUP BY ID
Since the inner CASE only gets a value when the WHEN condition is met, the MAX function will give you the value desired. This of course, only works as long as there are only unique QTypes per ID.
Generally using PIVOT in Sql-Server doesn't work in one step when your conditions are complex, specially when you need values from different columns. You could pivot your table in two queries and join those, but it would perform poorly and is less readable than my suggestion.
I have a table named "Orders"
It has following fields:
OrderID, OrderDate, ..... ,City, StatusID.
I want this result as return:
City No. of Delivered Orders, No. of Pending (Not Delivered)
-------------------------------------------------------------------
London 3 4
Paris 5 6
New York 7 8
Since we have only one field to track the delivery status that is StatusID, so I am facing difficulty in order to count for two conditions at a time..
Thanx in Advance :)
select City,
sum(case when StatusID = 'delivered' then 1 else 0 end) as [No. of Delivered Orders],
sum(case when StatusID = 'not_delivered' then 1 else 0 end) as [No. of Pending]
from Orders
I have a SQL Server 2012
And query
SELECT ManagerId,
SUM(CASE WHEN SoldInDay < 30 THEN 1 ELSE 0 END) as badSoldDays,
SUM(CASE WHEN Category = 'PC' THEN 1 ELSE 0 END) as DaysWithSoldPc
FROM SomeTable
GROUP BY ProductId
Some Table Definition
ManagerId | SoldInDay | Category
1 50 PC
1 20 Laptop
2 30 PC
3 40 Laptop
So, question is:
Does it mean that Sql will iterate over all rows twice? so, each aggregate function executes in separate cycle over all rows in table? or it's much smarter?
Doesn't matter what I want to get by this query, it's my dream.
(It appears that your question was addressed by a comment, but for completeness, I've provided an official answer here.)
First, your example SQL will not run. You are including ManagerId in the field list but not the GROUP BY. You will get an error akin to this:
Msg 8120, Level 16, State 1, Line 9 Column '#SomeTable.ManagerID' is
invalid in the select list because it is not contained in either an
aggregate function or the GROUP BY clause.
Assuming you meant "ManagerId" instead of "ProductId" in the field list, I reproduced your situation and reviewed the execution plans. It showed only one "Stream Aggregate" operator. You can force it to run over the table twice by separating the aggregations into two different common table expressions (CTEs) and JOINing them back together. In that case, you see two Stream Aggregate operators (one for each run through the table).
Here is the code to generate the execution plans:
DECLARE #SomeTable TABLE
(
ManagerId int,
SoldInDay int,
Category varchar(50)
);
INSERT INTO #SomeTable (ManagerId, SoldInDay, Category) VALUES (1, 50, 'PC');
INSERT INTO #SomeTable (ManagerId, SoldInDay, Category) VALUES (1, 20, 'Laptop');
INSERT INTO #SomeTable (ManagerId, SoldInDay, Category) VALUES (2, 30, 'PC');
INSERT INTO #SomeTable (ManagerId, SoldInDay, Category) VALUES (3, 40, 'Laptop');
/*
This produces an error:
Msg 8120, Level 16, State 1, Line 9
Column '#SomeTable.ManagerID' is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause.
SELECT
ManagerId,
SUM(CASE WHEN SoldInDay < 30 THEN 1 ELSE 0 END) as badSoldDays,
SUM(CASE WHEN Category = 'PC' THEN 1 ELSE 0 END) as DaysWithSoldPc
FROM #SomeTable
GROUP BY ProductId;
*/
SELECT
ManagerId,
SUM(CASE WHEN SoldInDay < 30 THEN 1 ELSE 0 END) as BadSoldDays,
SUM(CASE WHEN Category = 'PC' THEN 1 ELSE 0 END) as DaysWithSoldPc
FROM #SomeTable
GROUP BY ManagerId;
WITH DaysWithSoldPcTable AS
(
SELECT
ManagerId,
SUM(CASE WHEN Category = 'PC' THEN 1 ELSE 0 END) as DaysWithSoldPc
FROM #SomeTable
GROUP BY ManagerId
), BadSoldDaysTable AS
(
SELECT
ManagerId,
SUM(CASE WHEN SoldInDay < 30 THEN 1 ELSE 0 END) as BadSoldDays
FROM #SomeTable
GROUP BY ManagerId
)
SELECT
DaysWithSoldPcTable.ManagerId,
DaysWithSoldPcTable.DaysWithSoldPc,
BadSoldDaysTable.BadSoldDays
FROM DaysWithSoldPcTable
JOIN BadSoldDaysTable
ON DaysWithSoldPcTable.ManagerId = BadSoldDaysTable.ManagerId;
I'm working on an ssis package to fix some data from a table. The table looks something like this:
CustID FieldID INT_VAL DEC_VAL VARCHAR_VAL DATE_VAL
1 1 23
1 2 500.0
1 3 David
1 4 4/1/05
1 5 52369871
2 1 25
2 2 896.23
2 3 Allan
2 4 9/20/03
2 5 52369872
I want to transform it into this:
CustID FirstName AccountNumber Age JoinDate Balance
1 David 52369871 23 4/1/05 500.0
2 Allan 52369872 25 9/20/03 896.23
Currently, I've got my SSIS package set up to pull in the data from the source table, does a conditional split on the field id, then generates a derived column on each split. The part I'm stuck on is joining the data back together. I want to join the data back together on the CustId.
However, the join merge only allows you to join 2 datasets, in the end I will need to join about 30 data sets. Is there a good way to do that without having to have a bunch of merge joins?
That seems a bit awkward, why not just do it in a query?
select
CustID,
max(case when FieldID = 3 then VARCHAR_VAL else null end) as 'FirstName',
max(case when FieldID = 5 then INT_VAL else null end) as 'AccountNumber',
max(case when FieldID = 1 then INT_VAL else null end) as 'Age',
max(case when FieldID = 4 then DATE_VAL else null end) as 'JoinDate',
max(case when FieldID = 2 then DEC_VAL else null end) as 'Balance'
from
dbo.StagingTable
group by
CustID
If your source system is MSSQL, then you can use that query from SSIS or even create a view in the source database (if you're allowed to). If not, then copy the data directly to a staging table in MSSQL and query it from there.