Need single row in sql query despite row count is 0 - sql

Below is my query which returns no row and this is correct as per database records.
SELECT CustomerID ,
'Forwarder' AS CustType ,
RForLocation ,
YEAR(ReceivedDate) AS CurrentYear ,
CONVERT(VARCHAR, COUNT(CASE WHEN MONTH(ReceivedDate) = 1 THEN 1
ELSE 0
END)) AS Jan
FROM RootTable
WHERE Customerid = 12742
AND YEAR(ReceivedDate) = 2014
GROUP BY CustomerID ,
RForLocation ,
YEAR(ReceivedDate)
But my requirement is that I should get a blank row with customerId, CustType,CurrentYear and Jan Count as Zero (0)
Below is my requirement
CustomerId CustType CurrentYear Jan
12742 Forwarder 2014 0
Thanks
Please Help

SELECT CustomerID
,'Forwarder' AS CustType
,RForLocation
,YEAR(ReceivedDate) AS CurrentYear
,ISNULL(
NULLIF(
COUNT(CASE WHEN MONTH(ReceivedDate) = 1 THEN 1 ELSE 0 END)
, 0)
, '')
AS Jan
FROM RootTable
WHERE Customerid = 12742
AND YEAR(ReceivedDate) = 2014
GROUP BY CustomerID ,
RForLocation ,
YEAR(ReceivedDate)

Whatever I could understand, please find the script and the updated query, with which, I tried it.
Created the table:
Create table RootTable
(CustomerID int,
RForLocation varchar(20),
ReceivedDate datetime)
Inserted the data:
insert into RootTable
Values(1, 'A', '1-Jan-2013'),
(2, 'A', '1-Feb-2013'),
(3, 'B', '1-Jan-2013'),
(4, 'B', '1-Mar-2013')
Query to fetch the records :
SELECT CustomerID ,
'Forwarder' AS CustType ,
RForLocation ,
'2014' AS CurrentYear ,
CONVERT(VARCHAR, COUNT(CASE WHEN MONTH(ReceivedDate) = 1 AND YEAR(ReceivedDate) = 2014 THEN 1
ELSE null
END)) AS Jan
FROM RootTable
WHERE Customerid = 1
GROUP BY CustomerID ,
RForLocation;
Since I want record for customer even if he does not have any record for specific year, I don't need to give condition of date in where clause, instead I can do the counting based on year, which will give me required output.
Hope it helps.

Change your CASE statement
CONVERT(VARCHAR, CASE WHEN count(MONTH(ReceivedDate) = 1)
THEN count(MONTH(ReceivedDate) = 1)
ELSE 0
end

SELECT
RootTable.CustomerID ,
'Forwarder' AS CustType ,
RForLocation ,
YEAR(ReceivedDate) AS CurrentYear,
SUB1.MON_COL,
CASE WHEN sub1.CNT IS NULL THEN 0 ELSE sub1.CNT END AS CNT
FROM RootTable
LEFT JOIN
(
SELECT COUNT(1) CNT, CustomerId,MONTH(ReceivedDate) AS MON_COL,YEAR(ReceivedDate) AS YEAR_COL
FROM RootTable
GROUP BY CustomerId,MONTH(ReceivedDate),YEAR(ReceivedDate)
) sub1 ON sub1.CustomerID = RootTable.CustomerId AND MONTH(ReceivedDate) = sub1.MON_COL AND YEAR(ReceivedDate) = sub1.YEAR_COL
WHERE RootTable.CustomerId=2

Related

How to get count of a particular row

I have table that contain Id,Date and Status i.e open/close
i just want a result in sql that contain month wise open,close and total count of Id's
e.g In Jan open count 15,close count 5 and total count 20
Use RollUp() and Group By as below:
;WITH T AS
(
SELECT
Id,
DATENAME(MONTH,[Date]) AS [MonthName],
Status
FROM #tblTest
)
SELECT
[MonthName],
[Status],
StatusCount
FROM
(
SELECT
MonthName,
CASE ISNULL(Status,'') WHEN '' THEN 'Total' ELSE Status END AS Status,
Count(Status) AS StatusCount
FROM T
GROUP BY ROLLUP([MonthName],[Status])
)X
WHERE X.MonthName IS NOT NULL
ORDER BY X.[MonthName],X.[Status]
Output:
Note: If required data in single row by month then apply PIVOT
select year(date), month(date),
sum(case when status = 'open' then 1 else 0 end) as open_count,
sum(case when status = 'closed' then 1 else 0 end) as closed_count,
count(*) as total_count
from your_table
group by year(date), month(date)

Sql ISNULL condition in Sql Pivot and Sql case

I searched for many solutions on SO and elsewhere but couldn't quite understand how to write a query for my problem.
Anyway my query looks like below
SELECT * FROM
(
SELECT Id, Date, Name, Amount,
CASE
WHEN DATEDIFF(DAY,Date,GETDATE()) <=0
THEN 'Current'
WHEN DATEDIFF(DAY,Date,GETDATE()) <30
THEN 'Due30'
WHEN DATEDIFF(DAY,Date,GETDATE()) <60
THEN 'Due60'
ELSE 'Due90'
END AS [Age]
FROM Statement
WHERE (Amount <> 0)
) AS S
PIVOT
(
SUM(Amount)
FOR[Age] IN ([Current],[Due30],[Due60],[Due90])
) P
and the result looks like this
Id Date Name Current Due30 Due60 Due90
----------- ---------- --------------------------------------------
1 2016-04-03 Alan NULL NULL NULL 110.00
2 2016-05-02 TC NULL NULL 30.00 NULL
where should i insert IsNull condition to be able to remove the null in the result and add a zero there.
I tried inserting IsNull in the pivot query but we all know that is not meant to work
You have to add it repetitively in the final SELECT, when you replace the SELECT * (which should only exist in ad-hoc queries or EXISTS tests) with the column list:
SELECT
Id,
Date,
Name,
COALESCE([Current],0) as [Current],
COALESCE(Due30,0) as Due30,
COALESCE(Due60,0) as Due60,
COALESCE(Due90,0) as Due90
FROM
(
SELECT Id, Date, Name, Amount,
CASE
WHEN DATEDIFF(DAY,Date,GETDATE()) <=0
THEN 'Current'
WHEN DATEDIFF(DAY,Date,GETDATE()) <30
THEN 'Due30'
WHEN DATEDIFF(DAY,Date,GETDATE()) <60
THEN 'Due60'
ELSE 'Due90'
END AS [Age]
FROM Statement
WHERE (Amount <> 0)
) AS S
PIVOT
(
SUM(Amount)
FOR[Age] IN ([Current],[Due30],[Due60],[Due90])
) P
I've also used COALESCE since it's generally the preferred option (ANSI standard, extends to more than two arguments, applies normal type precedence rules) instead of ISNULL.
SELECT Id
, [Date]
, Name
, [Current] = SUM(CASE WHEN val <= 0 THEN Amount ELSE 0 END)
, Due30 = SUM(CASE WHEN val < 30 THEN Amount ELSE 0 END)
, Due60 = SUM(CASE WHEN val < 60 THEN Amount ELSE 0 END)
, Due90 = SUM(CASE WHEN val >= 60 THEN Amount ELSE 0 END)
FROM dbo.[Statement] t
CROSS APPLY (
SELECT val = DATEDIFF(DAY, [Date], GETDATE())
) s
WHERE Amount <> 0
GROUP BY Id, [Date], Name

Rewrite SQL query to return rows for each dataset

I have the following table for student's fee payments
[fee_id] INT
[user_id] INT
[payment] DECIMAL (18, 2)
[date] DATETIME
[fee_remaining] DECIMAL (18, 2)
[year] INT
[payment_method] NVARCHAR (50)
[fee_required] DECIMAL (18, 2)
This is my current query to display the number of students who have either paid, yet to pay or have partially paid their fees for the year
SELECT DISTINCT
(SELECT COUNT(*) AS Expr1
FROM fee_payments
WHERE (fee_remaining = 0)
AND (YEAR = #year)) AS Fully_Paid,
(SELECT COUNT(*) AS Expr1
FROM fee_payments
WHERE (fee_remaining = fee_required)
AND (YEAR = #year)) AS Unpaid,
(SELECT COUNT(*) AS Expr1
FROM fee_payments
WHERE (fee_remaining > 0)
AND (YEAR = #year)
AND (fee_remaining <> fee_required)) AS Partially_Paid
FROM fee_payments AS fee_payments_1
This is my output
Fully_Paid | Unpaid | Partially_Paid
-------------------------------------
8 | 1 | 5
Is it at all possible to have my output displayed as follows?
Status | Total
----------------------------
Fully Paid | 8
Unpaid | 1
Partially Paid | 5
Any help would be greatly appreciated
Use a case expression to assign the required status to each row and group by the calculated column.
select status, count(*) as total
from (
SELECT
case when fee_remaining = 0 then 'fully_paid'
when fee_remaining <> fee_required then 'partially_paid'
when fee_remaining = fee_required then 'unpaid'
end as status
FROM fee_payments
WHERE YEAR = #year) t
group by status
Also note this assumes fee_remaining and fee_required are non null values. If they can be null, use coalesce to handle them when comparing.
So without completely restructuring your query into something more efficient like kvp's answer, you could UNION each of the results instead of using them each as a sub-query:
SELECT 'Fully Paid' AS Status, COUNT(*) AS Total
FROM fee_payments
WHERE (fee_remaining = 0) AND (YEAR = #year)
UNION
SELECT 'Unpaid', COUNT(*)
FROM fee_payments
WHERE (fee_remaining = fee_required) AND (YEAR = #year)
UNION
SELECT 'Partially Paid', COUNT(*)
FROM fee_payments
WHERE (fee_remaining > 0) AND (YEAR = #year) AND (fee_remaining <> fee_required)
Your code appears to have more than one row per year. It seems like the last row would be the most informative, so I'm thinking something like this:
select sum(case when fee_remaining = 0 then 1 else 0 end) as FullyPaid,
sum(case when fee_remaining < fee_required then 1 else 0 end) as PartiallyPaid,
sum(case when fee_remaining = fee_required then 1 else 0 end) as Unpaid
from (select fp.*,
row_number() over (partition by user_id order by date desc) as seqnum
from fee_payments fp
where YEAR = #year
) fp
where seqnum = 1;
SELECT 'Fully Paid' as Status, COUNT(*) AS Total
FROM fee_payments
GROUP BY year
WHERE fee_remaining = 0
AND YEAR = #year
UNION ALL
SELECT 'Unpaid' as Status, COUNT(*) AS Total
FROM fee_payments
GROUP BY year
WHERE fee_remaining = fee_required
AND YEAR = #year
UNION ALL
SELECT 'Partially Paid' as Status, COUNT(*) AS Total
FROM fee_payments
GROUP BY year
WHERE fee_remaining > 0
AND YEAR = #year
AND fee_remaining <> fee_required

Annual Count by Criteria

I am working on a project with our HR department.
I have a table called [EEMaster] that keeps a record of the Active/Termed employees.
It is updated from a flat File using a Slowly Changing Dimension.
At the end of the year I need a count of the number of Active employees and the number of termed employees and then the year.
Here is an example of the data I need returned annually.
| 2010 | 2011 | 2012 | 2013 |
HistoricalHC | 447 | 419 | 420 | 418 |
NumbTermEmp | 57 | 67 | 51 | 42 |
I currently have the data connected to an excel spreadsheet providing a rolling count by Division. I use the following columns from the [EEMaster] for it.
ChangeStatus (1/0 from the SCD)
EmpStatusName ("Active" for current employees and "Withdrawn" for Termed Employees)
HireYear (set to All in the pivot table)
Term Year (set to 2013 in the pivot table)
PONumb (The employee numbers, I use for the count)
I have created a table to input the data into, I will manually load the previous years (counts)into the table since the current development is a rolling number. What I want to do is to develop an SSIS package that will capture the count on Jan 1 of 2014 and insert the # of "Active Employees", "Termed Employees" and the Year that just finished into a table.
UPDATE:
I have created two queries. One that provides the number of Active Employees
SELECT COUNT([PersNo]) AS HistoricalHC
FROM [dbo].[EEMaster]
WHERE [ChangeStatus] = 'Current' AND [EmpStatusName] = 'Active'
it returns
|HistoricHC|
|418 |
And another that provides the number of terms by Term Year
SELECT COUNT([PersNo]) AS NumbOfTermEE
FROM [dbo].[EEMaster]
WHERE [ChangeStatus] = 'Current' AND [EmpStatusName] = 'Withdrawn'
AND [TermYear] = '2013'
it returns
|NumbOfTermEE|
|42 |
I need the [TermYear] to be dynamic. Since this will run on Jan 1st of every year. It would need to pull the number of terms for the previous year (continually).
Then I need both of these numbers to be added into the new row with the year the data was calculated.
|Year|HistoricalHC|NumbOfTermEmp|
|2010|447 |57 |
|2011|419 |67 |
|2012|420 |51 |
|2013|418 |42 |
You are looking for a syntax of a case expression that does an aggregate to add a few for different things.
Sum(Case when (expression) then 1 end)
You also want to group by year it seems in the columns so you can easily pivot on that. You mention dynamic but I don't really know if you need to get much dynamic for just the year logic. I am not really getting if you want a SQL statement to go in a data flow to generate an output of an Excel sheet or not. Basically if you want just a grid with one row being one set of conditions and another being another. I would do a 'union' of two or more selects as long as it is not too large it should not be that hard. Here is a simple self extracting example with dummy data to see what I mean more.
It will run as is in SQL Management Studio 2005 and up.
declare #Person Table ( personID int identity, person varchar(8));
insert into #Person values ('Brett'),('Sean'),('Chad'),('Michael'),('Ray'),('Erik'),('Queyn');
declare #Orders table ( OrderID int identity, PersonID int, OrderCnt int, dt Date);
insert into #Orders values (1, 10, '1-7-11'),(1, 12, '2-12-12'),(2, 20, '7-1-13'),(2, 12, '1-5-10'),(3, 20, '6-4-11')
,(3, 12, '2-3-10'),(3, 6, '6-10-10'),(4, 20, '7-10-11'),(5, 20, '1-8-10'),(5, 9, '2-10-11'),
(6, 20, '3-1-11'),(6, 34, '4-6-12'),(7, 20, '5-1-11'),(7, 12, '6-8-12'),(7, 56, '7-25-13')
-- As is just joining sets
select *
from #Person p
join #Orders o on p.personID = o.PersonID
order by dt
-- Years on the rows
select
year(o.dt) as Year
, sum(o.OrderCnt) as Orders
, count(p.personID) as People
, count(distinct p.personID) as DistinctPeople
from #Person p
join #Orders o on p.personID = o.PersonID
group by year(o.dt)
-- Custom grouping on rows and doing the years with pivots for the columns
Select
'BulkOrders' as Description
, sum(case when year(o.dt) = '2010' then OrderCnt end) as [2010Orders]
, sum(case when year(o.dt) = '2011' then OrderCnt end) as [2011Orders]
, sum(case when year(o.dt) = '2012' then OrderCnt end) as [2012Orders]
, sum(case when year(o.dt) = '2013' then OrderCnt end) as [2013Orders]
, sum(OrderCnt) as Totals
from #Person p
join #Orders o on p.personID = o.PersonID
union
select
'OrdersByPerson'
, Count(case when year(o.dt) = '2010' then p.personID end)
, Count(case when year(o.dt) = '2011' then p.personID end)
, Count(case when year(o.dt) = '2012' then p.personID end)
, Count(case when year(o.dt) = '2013' then p.personID end)
, Count(p.personID)
from #Person p
join #Orders o on p.personID = o.PersonID
union
select
'OrdersByPersonDistinct'
, Count(distinct case when year(o.dt) = '2010' then p.personID end)
, Count(distinct case when year(o.dt) = '2011' then p.personID end)
, Count(distinct case when year(o.dt) = '2012' then p.personID end)
, Count(distinct case when year(o.dt) = '2013' then p.personID end)
, Count(distinct p.personID)
from #Person p
join #Orders o on p.personID = o.PersonID
Here is the solution I cam up with.
I will create an SSIS package that will run the following Stored Procedure.
INSERT INTO [dbo].[TORateFY] (Year,HistoricalHC,NumbTermedEmp)
SELECT DISTINCT YEAR(GETDATE()) AS [Year],
SUM(CASE WHEN EmpStatusName = 'Active' THEN 1 ELSE 0 END) AS HistoricalHC,
SUM(CASE WHEN EmpStatusName = 'Withdrawn' AND TermYear = YEAR(GETDATE()) THEN 1 ELSE 0 END) AS NumbOfTermEE
FROM dbo.EEMaster
This will be scheduled to run Annually on the 31st of Dec.
Update to my previous Answer:
I worked with a guy on another forum and he provided an excellent script that will give the correct count for both the active and termed employees on a monthly basis instead of waiting until the end of the year to get an overall count. This puts the reporting more inline with what was originally done manually.
MERGE dbo.TORateFY AS tgt
USING (
SELECT DATENAME(YEAR, GETDATE()) AS [Year],
SUM(CASE WHEN EmpStatusName = 'Active' THEN 1 ELSE 0 END) AS HistoricalHC,
SUM(CASE WHEN EmpStatusName = 'Withdrawn' AND TermYear = DATENAME(YEAR,
GETDATE()) THEN 1 ELSE 0 END) AS NumbOfTermEE
FROM dbo.EEMaster
WHERE ChangeStatus = 'Current'
AND EmpStatusName IN ('Active', 'Withdrawn')
OR TermYear <= DATENAME(YEAR, GETDATE())
) AS src ON src.[Year] = tgt.[Year]
WHEN MATCHED
THEN UPDATE
SET tgt.HistoricalHC = src.HistoricalHC,
tgt.NumbTermedEmp = src.NumbOfTermEE
WHEN NOT MATCHED BY TARGET
THEN INSERT (
[Year],
HistoricalHC,
NumbTermedEmp
)
VALUES (
src.[Year],
src.HistoricalHC,
src.NumbOfTermEE
);
I wanted to share the in case anyone else ran into a similar situation.
Thank you everyone for your input and guidance.

How do I use this SQL Stored Procedure to create an INSERT statement?

A workmate has given me this stored procedure and asked for help to create an sql INSERT statement based on its results, but am a little confused to some of the methods. For example, I've never seen a CASE statement in sql. I looked it up but the useage is different from what I was given.
Here is the stored procedure
if #ScheduleType Is Null
SELECT Lu_Schedule_Types.ScheduleType,
SUM(CASE WHEN InductionProduction = 1 THEN CASE WHEN (LEFT(DATENAME(Month,
3)) = 'JAN' THEN ItemQty END END) AS I01,
SUM(CASE WHEN InductionProduction = 1 THEN CASE WHEN (LEFT(DATENAME(Month,
Item_Schedule.ItemScheduleDate), 3))
= 'FEB' THEN ItemQty END END) AS I02,
SUM(CASE WHEN InductionProduction = 2 THEN ItemQty ELSE 0 END) AS PRD,
LmpProjectInfo.PlannedQty,
LmpProjectInfo.AuthorizedQty,
LmpProjectInfo.WbsElementID
FROM Item_Schedule
INNER JOIN Lu_Schedule_Types
ON Item_Schedule.ItemScheduleType = Lu_Schedule_Types.ScheduleTypeId
RIGHT OUTER JOIN LmpProjectInfo
ON Item_Schedule.ItemWbsElement = LmpProjectInfo.WbsElementID
WHERE
(Item_Schedule.IsActive = 1)
AND (Item_Schedule.ItemWbsElement = #WbsElement)
AND (Item_Schedule.ItemScheduleDate < DATEADD(d, 1, #EndDate)) AND
(Item_Schedule.ItemScheduleDate >= #StartDate)
GROUP BY Lu_Schedule_Types.ScheduleType
, Lu_Schedule_Types.ScheduleTypeId
, LmpProjectInfo.PlannedQty
, LmpProjectInfo.AuthorizedQty
, LmpProjectInfo.WbsElementID
ORDER BY Lu_Schedule_Types.ScheduleTypeId
I am supposed to be helping him out but I'm by far a database wizard, and am a little out of my depth here. I'd really appreciate the help/advice/direction.
Thanks much!
This is quick sample that might work for you. This assumes that the table you want to insert data into takes all of the values return from the SELECT statement and that they are of the same type.
As a side note, the reason you might have got a bit confused about the CASE statements is possibly due to the fact there are two main ways to use them in SQL. CASE WHEN... like you have here and CASE #value# WHEN #result# THEN.... A little more searching on the web will lead you to some nice examples. For example this one.
INSERT INTO TABLE_NAME
(
ScheduleType,
I01,
I02,
PRD,
PlannedQty,
AuthorizedQty,
WbsElementID
)
SELECT
Lu_Schedule_Types.ScheduleType,
SUM(CASE WHEN InductionProduction = 1 THEN CASE WHEN (LEFT(DATENAME(Month,
3)) = 'JAN' THEN ItemQty END END) AS I01,
SUM(CASE WHEN InductionProduction = 1 THEN CASE WHEN (LEFT(DATENAME(Month, Item_Schedule.ItemScheduleDate), 3))
= 'FEB' THEN ItemQty END END) AS I02,
SUM(CASE WHEN InductionProduction = 2 THEN ItemQty ELSE 0 END) AS PRD,
LmpProjectInfo.PlannedQty,
LmpProjectInfo.AuthorizedQty,
LmpProjectInfo.WbsElementID
FROM
Item_Schedule INNER JOIN
Lu_Schedule_Types ON Item_Schedule.ItemScheduleType = Lu_Schedule_Types.ScheduleTypeId RIGHT OUTER JOIN
LmpProjectInfo ON Item_Schedule.ItemWbsElement = LmpProjectInfo.WbsElementID
WHERE
(Item_Schedule.IsActive = 1) AND (Item_Schedule.ItemWbsElement = #WbsElement) AND
(Item_Schedule.ItemScheduleDate < DATEADD(d, 1, #EndDate)) AND
(Item_Schedule.ItemScheduleDate >= #StartDate)
GROUP BY Lu_Schedule_Types.ScheduleType, Lu_Schedule_Types.ScheduleTypeId,
LmpProjectInfo.PlannedQty, LmpProjectInfo.AuthorizedQty,
LmpProjectInfo.WbsElementID
ORDER BY Lu_Schedule_Types.ScheduleTypeId
Try this one -
INSERT INTO dbo.table1 -- insert in table
(
ScheduleType
, I01
, I02
, PRD
, PlannedQty
, AuthorizedQty
, WbsElementID
)
SELECT
t.ScheduleType
, I01 = SUM(CASE WHEN InductionProduction = 1 AND MONTH(s.ItemScheduleDate) = 1 THEN ItemQty END)
, I02 = SUM(CASE WHEN InductionProduction = 1 AND MONTH(s.ItemScheduleDate) = 2 THEN ItemQty END)
, PRD = SUM(CASE WHEN InductionProduction = 2 THEN ItemQty END)
, i.PlannedQty
, i.AuthorizedQty
, i.WbsElementID
--INTO #temp_table -- or insert in temp table
FROM dbo.Item_Schedule s
JOIN dbo.Lu_Schedule_Types t ON s.ItemScheduleType = t.ScheduleTypeId
RIGHT JOIN dbo.LmpProjectInfo i ON s.ItemWbsElement = i.WbsElementID
WHERE s.IsActive = 1
AND s.ItemWbsElement = #WbsElement
AND s.ItemScheduleDate < DATEADD(d, 1, #EndDate))
AND s.ItemScheduleDate >= #StartDate
GROUP BY
t.ScheduleType
, t.ScheduleTypeId
, i.PlannedQty
, i.AuthorizedQty
, i.WbsElementID
ORDER BY t.ScheduleTypeId