SUM query of a UNION ALL off by 1 - sql

I have a query in which a table is created and left joined with a a WITH name AS() That portion of the query runs just fine and returns results as expected in the following format:
VISIT_ID | ACTUAL WEIGHT | STATED WEIGHT | WAS WEIGHT INFO OBTAINED?
123 | 1 | 0 | 0
321 | 0 | 0 | 1
567 | 0 | 1 | 0
...
I then use add a UNION ALL to it to obtain totals at the bottom of the columns. The columns ACTUAL WEIGHT and STATED WEIGHT are adding up just fine but the WAS WEIGHT INFO OBTAINED? column is off by 1.
The columns are flags 1 and 0, the third column will produce a 1 if both columns [ACTUAL WEIGHT] AND [STATED WEIGHT] are both NULL
Here is my query:
-- VARIABLE DECLARATION AND INITIALIZATION
DECLARE #SD DATE;
DECLARE #ED DATE;
SET #SD = '2013-12-16'
SET #ED = '2013-12-16';
-- THIS CREATES A TABLE WHERE ALL THE DESIRED VISIT ID NUMBERS WILL GO
-- THIS TABLE IS A UNIQUE CLUSTER
CREATE TABLE #T1
(
VISIT_ID VARCHAR(20) UNIQUE CLUSTERED
)
-- WHAT GETS INSERTED INTO #T1. IT IS QUICKER TO USE #T1 THAN #T1
INSERT INTO #T1
SELECT DISTINCT PTNO_NUM
FROM smsdss.BMH_PLM_PtAcct_V
WHERE Adm_Date BETWEEN #SD AND #ED
AND Plm_Pt_Acct_Type = 'I'
AND PtNo_Num < '20000000'
OPTION(RECOMPILE);
--+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
WITH WT AS
(SELECT EPISODE_NO,
MAX(CASE
WHEN FORM_USAGE = 'Admission'
AND OBSV_CD = 'A_WEIGHTOBTAINED'
AND DSPLY_VAL = 'Actual'
THEN 1
END) AS [ACTUAL WEIGHT],
MAX(CASE
WHEN FORM_USAGE = 'ADMISSION'
AND OBSV_CD = 'A_WEIGHTOBTAINED'
AND DSPLY_VAL = 'STATED'
THEN 1
END) AS [STATED WEIGHT]
FROM smsmir.obsv
WHERE form_usage = 'ADMISSION'
GROUP BY episode_no
)
SELECT T1.VISIT_ID
, ISNULL(WT.[ACTUAL WEIGHT], 0) AS [ACTUAL WEIGHT]
, ISNULL(WT.[STATED WEIGHT], 0) AS [STATED WEIGHT]
, CASE
WHEN [ACTUAL WEIGHT] IS NULL AND [STATED WEIGHT] IS NULL
THEN 1
ELSE 0
END AS [WAS WEIGHT INFO OBTAINED?]
FROM #T1 T1
LEFT JOIN WT WT
ON T1.VISIT_ID = WT.episode_no
UNION ALL
SELECT 'TOTALS'
, SUM(WT.[ACTUAL WEIGHT])
, SUM(WT.[STATED WEIGHT])
, SUM(
CASE
WHEN [ACTUAL WEIGHT] IS NULL AND [STATED WEIGHT] IS NULL
THEN 1
ELSE 0
END)
FROM #T1 T1
JOIN WT WT
ON T1.VISIT_ID = WT.[episode_no]
DROP TABLE #T1
Any info on why this is happening would be great.
I would like the info to all come out meaning I am trying to obtain the following:
VISIT_ID | ACTUAL WEIGHT | STATED WEIGHT | WAS WEIGHT INFO OBTAINED?
123 | 1 | 0 | 0
321 | 0 | 0 | 1
567 | 0 | 1 | 0
...
TOTALS | 10 | 25 | 9

There is a discrepancy in your row code vs your total code.
The former uses OUTER LEFT JOIN, while the latter - more restrictive INNER JOIN.

Related

Creating a new column for -ve values from the existing rows

How to write logic for this in SQL Server:
Voucher # Name Amount
-------------------------
123 ABC 910
123 ABC -910
224 XYZ 600
Expected output
Voucher # Name Amount - (Amount)
-------------------------------------------
123 ABC 910 -910
224 XYZ 600 -
Using conditional aggregation is really simple here. No need to query the same table over and over.
select Voucher
, Name
, Amount = sum(case when Amount > 0 then Amount else 0 end)
, [-Amount] = sum(case when Amount < 0 then Amount else 0 end)
from YourTable
group by Voucher
, Name
Here is the code, try it
Group by negative and positive amount and make a left join
DECLARE #tbl TABLE
(
Voucher varchar(10),
Name varchar(100),
Amount int
)
INSERT INTO #tbl
(
Voucher,
Name,
Amount
)
VALUES
(123,'ABC',910),
(123,'ABC',-910),
(224,'XYZ',600)
SELECT t1.Voucher, t1.Name, t1.Amount , ISNULL(t2.Amount,0) AS [(-Amount)] FROM (
SELECT t.Voucher, Name, Sum(t.Amount) Amount
FROM #tbl t
WHERE t.Amount > 0
GROUP BY t.Voucher, t.Name) t1 Left JOIN
(SELECT t.Voucher, Name, Sum(t.Amount) Amount
FROM #tbl t
WHERE t.Amount < 0
GROUP BY t.Voucher, t.Name) t2 ON t1.Voucher = t2.Voucher
If for each (Voucher, Name) there exists only 1 positive Amount and 0 or 1 negative Amount then with a self join:
select t.*, tt.amount NegativeAmount
from tablename t left join tablename tt
on tt.voucher = t.voucher and tt.name = t.name and tt.amount < 0
where t.amount > 0
See the demo.
Results:
> voucher | name | amount | NegativeAmount
> ------: | :--- | -----: | -------------:
> 123 | ABC | 910 | -910
> 224 | XYZ | 600 |

Formatting multiple SELECT statements with PIVOT

Currently have a script that unions around a dozen SELECT statements, and example of two of these along with an example of the results is shown below.
DECLARE #Age TABLE (name VARCHAR(30), total FLOAT, percentage FLOAT)
INSERT INTO #Age
SELECT '0-18', (SELECT COUNT(*) FROM tblPerson p
INNER JOIN tblClient c ON c.intPersonID = p.intPersonID
WHERE ISNULL(dbo.fncReportClient_Age(p.dteBirthdate, GETDATE()), '') >= 0 AND ISNULL(dbo.fncReportClient_Age(p.dteBirthdate, GETDATE()), '') <= 18), ''
UPDATE #Age
SET percentage = ROUND((SELECT total FROM #Age WHERE name = '0-18')/(SELECT SUM(total) FROM #Age) * 100, 2)
FROM #Age
WHERE name = '0-18'
Etc.
SELECT
g.nvhGenderName,
COUNT(*),
ROUND(COUNT(*) * 1.0 / SUM(COUNT(*)) OVER () * 100, 2)
FROM
tblClient c
LEFT JOIN tblPerson p ON p.intPersonID = c.intPersonID
LEFT JOIN tblGender g ON g.intGenderID = p.intGenderID
GROUP BY g.nvhGenderName
UNION ALL
SELECT * FROM #Age
Results example below:
Name | Total | % |
---------------------------------
Male | 6514 | 60.32 |
Female | 4285 | 39.68 |
0-18 | 279 | 1.58 |
19-24 | 1748 | 9.93 |
25-34 | 5423 | 30.80 |
35-64 | 9546 | 54.21 |
65+ | 614 | 3.50 |
I would like to display these results horizontally as opposed to vertically, I think it is possible to do this with PIVOT but have never really used them. An example of how I want the data to be displayed is shown below:
Gender | Total | % | Age | Total | % |
-------------------------------------------------------------
Male | 6514 | 60.32 | 0-18 | 279 | 1.58 |
Female | 4285 | 39.68 | 19-24 | 1748 | 9.93 |
| | | 25-34 | 5423 | 30.80 |
| | | 35-64 | 9546 | 54.21 |
| | | 65+ | 614 | 3.50 |
In particular I am not sure how I would use a pivot to combine the multiple (12) SELECT statements that require it.
Any help on how to format this would be much appreciated.
Whilst I believe the layout really should be achieved elsewhere, the following may work for you. Clearly I cannot test it so, without the benefit of testing here goes:
WITH myCTE AS (
SELECT
COUNT(CASE WHEN oa.age >= 0 AND oa.age < 19 THEN p.intPersonID ELSE NULL END) c0019
, COUNT(CASE WHEN oa.age >= 19 AND oa.age < 25 THEN p.intPersonID ELSE NULL END) c1925
, COUNT(CASE WHEN oa.age >= 25 AND oa.age < 35 THEN p.intPersonID ELSE NULL END) c2535
, COUNT(CASE WHEN oa.age >= 35 AND oa.age < 65 THEN p.intPersonID ELSE NULL END) c3565
, COUNT(CASE WHEN oa.age >= 65 THEN p.intPersonID ELSE NULL END) c65on
, COUNT(CASE WHEN g.nvhGenderName = 'Male' THEN p.intPersonID ELSE NULL END) cmale
, COUNT(CASE WHEN g.nvhGenderName = 'Female' THEN p.intPersonID ELSE NULL END) cfemale
, COUNT(*) ctotal
FROM tblClient c
LEFT JOIN tblPerson p ON p.intPersonID = c.intPersonID
OUTER APPLY (
SELECT
dbo.fncReportClient_Age(p.dteBirthdate) AS age
) AS oa
LEFT JOIN tblGender g ON g.intGenderID = p.intGenderID
)
SELECT
ca.Gender, ca.Total2, ca.Pct, ca.Age, ca.Total2, ca.Pct2
FROM myCTE
CROSS APPLY (
VALUES
(1, 'Male' , t.cmale , (cmale * 100.0 / ctotal), '0-18', c0019, (c0019 * 100.0 / ctotal))
, (2, 'Female',t.cfemale,(cfemale * 100.0 / ctotal), '19-24', c1925, (c1925 * 100.0 / ctotal))
, (3, NULL,NULL,NULL, '25-34', c2535, (c2535 * 100.0 / ctotal))
, (4, NULL,NULL,NULL, '35-64', c3565, (c3565 * 100.0 / ctotal))
, (5, NULL,NULL,NULL, '65+' , c65on, (c65on * 100.0 / ctotal))
) AS ca (rn, Gender, Total2, Pct, Age, Total2, Pct2)
ORDER BY ca.rn
;
The second (lower) part of the query above uses a technique for unpivoting data that combines cross apply with values and this allows us to "layout" each row of the wanted final result row by row.
Using a CTE (the upper part of the query above) isn't essential, it could be moved into a subquery instead, but the query inside the CTE should be trialed standalone first and it should produce all the numbers you need in one pass of the data (assuming that the gender table doesn't disturb the count results). Note that using count(case expression here) removes the need for multiple separate queries. I suspect you don't need left joins by the way, and if that is true you could also change the outer apply to a cross apply. Note that the apply is used to execute your function, and by doing it ths way we are able to reference the result of that function by an alias in the remainder of the query (I used "age" as that alias).
I am unsure why you involve a client table when counting the persons table. I suspect it isn't needed. If my suspicions are correct the CTE detail could be replaced by this:
SELECT
COUNT(CASE WHEN oa.age >= 0 AND oa.age < 19 THEN 1 ELSE NULL END) c0019
, COUNT(CASE WHEN oa.age >= 19 AND oa.age < 25 THEN 1 ELSE NULL END) c1925
, COUNT(CASE WHEN oa.age >= 25 AND oa.age < 35 THEN 1 ELSE NULL END) c2535
, COUNT(CASE WHEN oa.age >= 35 AND oa.age < 65 THEN 1 ELSE NULL END) c3565
, COUNT(CASE WHEN oa.age >= 65 THEN 1 ELSE NULL END) c65on
, COUNT(CASE WHEN g.nvhGenderName = 'Male' THEN 1 ELSE NULL END) cmale
, COUNT(CASE WHEN g.nvhGenderName = 'Feale' THEN 1 ELSE NULL END) cfemale
, COUNT(*) ctotal
FROM tblPerson p
CROSS APPLY (
SELECT
dbo.fncReportClient_Age(p.dteBirthdate) AS age
) AS oa
INNER JOIN tblGender g ON g.intGenderID = p.intGenderID
Create VIEW view_name AS
SELECT * FROM (
select ColumnName1,ColumnName2 from TableName
) as s
PIVOT (
Sum(ColumnName2)
FOR ColumnName3 in ("Row1","Row2","Row3"
)
) As pvt
DROP VIEW view_name;
Select * from view_name

Is there a better way to flatten out a table to take up fewer rows by moving fields in rows with duplicate keys into empty (NULL) fields?

I have a table with the recorded date, time and quantity of each item a child was given. My end goal is to pivot on that data, but preserve each individual quantity being given out according to date/time and child.
This is easy to achieve without a pivot, but it still takes up an entire row for each instance. What I want, is to flatten out the results to take up fewer rows. There isn't a huge functional difference, I'm just doing this to take up less real estate in the report that will end up using this data.
Updated to include a query for sample data:
DECLARE #Items TABLE (Name VARCHAR(10), Date DATETIME, ItemID INT, Quantity INT)
INSERT INTO #Items VALUES ('Jimmy', '01/23/2017 10:00:00', 1, 2),
('Jimmy', '01/23/2017 12:00:00', 1, 1),
('Jimmy', '01/23/2017 15:00:00', 2, 2),
('Billy', '01/23/2017 09:00:00', 1, 1),
('Billy', '01/23/2017 10:00:00', 2, 3)
This is what my starting table looks like:
Name Date ItemID Quantity
Jimmy 2017-01-23 10:00:00.000 1 2
Jimmy 2017-01-23 12:00:00.000 1 1
Jimmy 2017-01-23 15:00:00.000 2 2
Billy 2017-01-23 09:00:00.000 1 1
Billy 2017-01-23 10:00:00.000 2 3
I use a join to sum up the quantities for each day, sort the quantities into their own respective columns, and then drop the time:
SELECT d.Name, CAST(d.Date AS DATE) AS Date,
SUM(CASE WHEN s.ItemID = 1 THEN s.Quantity ELSE NULL END) AS SumBooks,
SUM(CASE WHEN s.ItemID = 2 THEN s.Quantity ELSE NULL END) AS SumPencils,
MAX(CASE WHEN d.ItemID = 1 THEN d.Quantity ELSE NULL END) AS Books,
MAX(CASE WHEN d.ItemID = 2 THEN d.Quantity ELSE NULL END) AS Pencils
FROM #Items d
INNER JOIN #Items s ON s.Name = d.Name AND CAST(s.Date AS DATE) = CAST(d.Date AS DATE)
GROUP BY d.Name, d.Date
This is the resulting data:
Name Date SumBooks SumPencils Books Pencils
Billy 2017-01-23 1 3 1 NULL
Billy 2017-01-23 1 3 NULL 3
Jimmy 2017-01-23 3 2 2 NULL
Jimmy 2017-01-23 3 2 1 NULL
Jimmy 2017-01-23 3 2 NULL 2
This is the structure I am trying to achieve:
Name Date SumBooks SumPencils Books Pencils
Billy 2017-01-23 1 3 1 3
Jimmy 2017-01-23 3 2 2 2
Jimmy 2017-01-23 3 2 1 NULL
I was able to do this using a cursor to iterate over each row and check a new table for any matches of Date, Name, and Books = NULL. If a match was found, I update that row with the quantity. Else, I insert a new row with the Books quantity and a NULL value in the Pencils field, later to be updated with a Pencils quantity, and so on.
So, I am able to get the results I need, but this check has to be done for every item column. For just a couple items, it isn't a big deal. When there's a dozen or more items and the result has 30+ columns, it ends up being a lot of declared variables and large, repeating IF/ELSE statements.
I'm not sure if this is commonly done, but if it is, I'm lacking the proper verbiage to find out on my own. Thanks in advance for any Suggestions.
If we trade the inner join for an outer apply() or a left join
and include those values to the group by we can get the results you are looking for based on the test data provided.
;with cte as (
select
i.Name
, [Date] = convert(date,i.[Date])
, SumBooks = sum(case when ItemId = 1 then Quantity else null end)
, SumPencils = sum(case when ItemId = 2 then Quantity else null end)
, Books = b.Books
, Pencils = max(case when ItemId = 2 then Quantity else null end)
, rn = row_number() over (
partition by i.Name, convert(varchar(10),i.[Date],120)
order by b.booksdate
)
from #Items i
outer apply (
select Books = Quantity, BooksDate = b.[Date]
from #Items b
where b.ItemId = 1
and b.Name = i.Name
and convert(date,b.[Date])=convert(date,i.[Date])
) as b
group by
i.Name
, convert(date,i.[Date])
, b.Books
, b.BooksDate
)
select
Name
, Date
, SumBooks
, SumPencils
, Books
, Pencils = Pencils + case when rn > 1 then null else 0 end
from cte
alternate left join for b:
left join (
select Books = Quantity, BooksDate = b.[Date], Name, Date
from Items b
where b.ItemId = 1
) as b on b.Name = i.Name and convert(date,b.[Date])=convert(date,i.[Date])
test setup: http://rextester.com/IXHU81911
create table Items (
Name varchar(64)
, Date datetime
, ItemID int
, Quantity int
);
insert into Items values
('Jimmy','2017-01-23 10:00:00.000',1,2)
, ('Jimmy','2017-01-23 12:00:00.000',1,1)
, ('Jimmy','2017-01-23 13:00:00.000',1,1) /* Another 1 Book */
, ('Jimmy','2017-01-23 15:00:00.000',2,2)
, ('Billy','2017-01-23 09:00:00.000',1,1)
, ('Billy','2017-01-23 10:00:00.000',2,3)
, ('Zim' ,'2017-01-23 10:00:00.000',2,1) /* No books */
query:
;with cte as (
select
i.Name
, [Date] = convert(varchar(10),i.[Date],120)
, SumBooks = sum(case when ItemId = 1 then Quantity else null end)
, SumPencils = sum(case when ItemId = 2 then Quantity else null end)
, Books = b.Books
, Pencils = max(case when ItemId = 2 then Quantity else null end)
, rn = row_number() over (
partition by i.Name, convert(varchar(10),i.[Date],120)
order by b.booksdate
)
from Items i
outer apply (
select Books = Quantity, BooksDate = b.[Date]
from Items b
where b.ItemId = 1
and b.Name = i.Name
and convert(date,b.[Date])=convert(date,i.[Date])
) as b
group by
i.Name
, convert(varchar(10),i.[Date],120)
, b.Books
, b.BooksDate
)
select
Name
, Date
, SumBooks
, SumPencils
, Books
, Pencils = Pencils + case when rn > 1 then null else 0 end
from cte
note: convert(varchar(10),i.[Date],120) is used on rextester to override default formatting of date. Use convert(date,i.[Date]) or cast(i.[Date] as date) outside of rextester.
results:
+-------+------------+----------+------------+-------+---------+
| Name | Date | SumBooks | SumPencils | Books | Pencils |
+-------+------------+----------+------------+-------+---------+
| Billy | 2017-01-23 | 1 | 3 | 1 | 3 |
| Jimmy | 2017-01-23 | 4 | 2 | 1 | 2 |
| Jimmy | 2017-01-23 | 4 | 2 | 1 | NULL |
| Jimmy | 2017-01-23 | 4 | 2 | 2 | NULL |
| Zim | 2017-01-23 | NULL | 1 | NULL | 1 |
+-------+------------+----------+------------+-------+---------+

Select with CASE Statement SQL Server 2008 R2

I have a query that uses a CASE statement to score accounts. The query looks for values that are in a vector form. So for instance if I am a patient I can have multiple diagnosis codes, but they are not stored as column values, they are stored in another row like so:
VISIT_ID | CLASFCD
123 | 196.0
123 | 197.0
123 | 198.0
321 | 199.0
321 | 650.9
222 | 111
555 | ...
...
My query uses a Case Statment like so:
, CASE
WHEN DV.ClasfCd IN (
'196.0','196.1','196.2','196.3','196.5','196.6','196.8','196.9',
'197.0','197.1','197.2','197.3','197.4','197.5','197.6','197.7',
'197.8','198.2','198.3','198.4','198.5','199.1','209.7'
)
THEN 6
ELSE 0
END AS PRIN_DX_CD_5
I do this for 5 different groups of codes. What's happening is that if the criteria is met for one of those groups, the results come back in another row instead of on the same row. Here is an example of the data that I am getting back:
VISIT_ID | CC GROUP 1 | CC GROUP 2 | CC GROUP 3 | CC GROUP 4 | CC GROUP 5 | TOTAL
123 | 1 | 0 | 0 | 0 | 0 | 1
123 | 0 | 2 | 0 | 0 | 0 | 2
123 | 0 | 0 | 0 | 0 | 0 | 0
What I want returned is the following:
VISIT_ID | CC GROUP 1 | CC GROUP 2 | CC GROUP 3 | CC GROUP 4 | CC GROUP 5 | TOTAL
123 | 1 | 2 | 0 | 0 | 0 | 3
321 | 1 | 0 | 0 | 0 | 6 | 6
The final total score cannot exceed 6.
The entire query in some brevity is here, it is part of a multi-part query, I am making changes to the original:
SET ANSI_NULLS OFF
GO
DECLARE #SD DATETIME
DECLARE #ED DATETIME
SET #SD = '2013-01-01';
SET #ED = '2013-05-31';
-- #CM TABLE DECLARATION #############################################]
DECLARE #CM TABLE (
ENCOUNTER_ID VARCHAR(200)
, [MRN CM] VARCHAR(200)
, NAME VARCHAR(500)
, [CC GRP ONE SCORE] VARCHAR(20)
, [CC GRP TWO SCORE] VARCHAR(20)
, [CC GRP THREE SCORE] VARCHAR(20)
, [CC GRP FOUR SCORE] VARCHAR(20)
, [CC GRP FIVE SCORE] VARCHAR(20)
, [CC LACE SCORE] INT
)
--####################################################################]
INSERT INTO #CM
SELECT
C.PT_NO
, C.MED_REC_NO
, C.PT_NAME
, C.PRIN_DX_CD_1
, C.PRIN_DX_CD_2
, C.PRIN_DX_CD_3
, C.PRIN_DX_CD_4
, C.PRIN_DX_CD_5
, CASE
WHEN (C.PRIN_DX_CD_1+C.PRIN_DX_CD_2+C.PRIN_DX_CD_3+C.PRIN_DX_CD_4+C.PRIN_DX_CD_5) = 0 THEN 0
WHEN (C.PRIN_DX_CD_1+C.PRIN_DX_CD_2+C.PRIN_DX_CD_3+C.PRIN_DX_CD_4+C.PRIN_DX_CD_5) = 1 THEN 1
WHEN (C.PRIN_DX_CD_1+C.PRIN_DX_CD_2+C.PRIN_DX_CD_3+C.PRIN_DX_CD_4+C.PRIN_DX_CD_5) = 2 THEN 2
WHEN (C.PRIN_DX_CD_1+C.PRIN_DX_CD_2+C.PRIN_DX_CD_3+C.PRIN_DX_CD_4+C.PRIN_DX_CD_5) = 3 THEN 3
WHEN (C.PRIN_DX_CD_1+C.PRIN_DX_CD_2+C.PRIN_DX_CD_3+C.PRIN_DX_CD_4+C.PRIN_DX_CD_5) = 4 THEN 4
WHEN (C.PRIN_DX_CD_1+C.PRIN_DX_CD_2+C.PRIN_DX_CD_3+C.PRIN_DX_CD_4+C.PRIN_DX_CD_5) = 5 THEN 5
WHEN (C.PRIN_DX_CD_1+C.PRIN_DX_CD_2+C.PRIN_DX_CD_3+C.PRIN_DX_CD_4+C.PRIN_DX_CD_5) >= 6 THEN 6
END AS CC_LACE_SCORE
FROM (
SELECT DISTINCT PAV.PT_NO
, MED_REC_NO
, PT_NAME
, CASE
WHEN dv.ClasfCd IN (
)
THEN 1
ELSE 0
END AS PRIN_DX_CD_1
, CASE
WHEN DV.ClasfCd IN (
)
THEN 2
ELSE 0
END AS PRIN_DX_CD_2
, CASE
WHEN DV.ClasfCd IN (
)
THEN 3
ELSE 0
END AS PRIN_DX_CD_3
, CASE
WHEN DV.ClasfCd IN (
)
THEN 4
ELSE 0
END AS PRIN_DX_CD_4
, CASE
WHEN DV.ClasfCd IN (
)
THEN 6
ELSE 0
END AS PRIN_DX_CD_5
FROM smsdss.BMH_PLM_PtAcct_V PAV
JOIN smsdss.BMH_PLM_PtAcct_Clasf_Dx_V DV
ON PAV.PtNo_Num = DV.PtNo_Num
WHERE Dsch_Date BETWEEN #SD AND #ED
)C
GROUP BY C.PT_NO
, C.MED_REC_NO
, C.PT_NAME
, C.PRIN_DX_CD_1
, C.PRIN_DX_CD_2
, C.PRIN_DX_CD_3
, C.PRIN_DX_CD_4
, C.PRIN_DX_CD_5
ORDER BY C.Pt_No
SELECT * FROM #CM
thank you for your help,
The problem is that you are including the calculated PRIN_DX_ columns in the aggregation. Instead, remove them from the aggregation and just choose the non-0 value (using max()):
SELECT C.PT_NO, C.MED_REC_NO, C.PT_NAME,
max(C.PRIN_DX_CD_1) as PRIN_DX_CD_1,
max(C.PRIN_DX_CD_2) as PRIN_DX_CD_2,
max(C.PRIN_DX_CD_3) as PRIN_DX_CD_3,
max(C.PRIN_DX_CD_4) as PRIN_DX_CD_4,
max(C.PRIN_DX_CD_5) as PRIN_DX_CD_5,
(case when max(C.PRIN_DX_CD_1) + max(C.PRIN_DX_CD_2) + max(C.PRIN_DX_CD_3) +
max(C.PRIN_DX_CD_4) + max(C.PRIN_DX_CD_5) < 6
then max(C.PRIN_DX_CD_1) + max(C.PRIN_DX_CD_2) + max(C.PRIN_DX_CD_3) +
max(C.PRIN_DX_CD_4) + max(C.PRIN_DX_CD_5)
else 6
end) as CC_LACE_SCORE
FROM (SELECT DISTINCT PAV.PT_NO, MED_REC_NO, PT_NAME,
(CASE WHEN dv.ClasfCd IN ()
THEN 1
ELSE 0
END) AS PRIN_DX_CD_1,
(CASE WHEN DV.ClasfCd IN ()
THEN 2
ELSE 0
END) AS PRIN_DX_CD_2
(CASE WHEN DV.ClasfCd IN ()
THEN 3
ELSE 0
END) AS PRIN_DX_CD_3,
(CASE WHEN DV.ClasfCd IN ()
THEN 4
ELSE 0
END) AS PRIN_DX_CD_4,
(CASE WHEN DV.ClasfCd IN ()
THEN 6
ELSE 0
END) AS PRIN_DX_CD_5
FROM smsdss.BMH_PLM_PtAcct_V PAV join
smsdss.BMH_PLM_PtAcct_Clasf_Dx_V DV
ON PAV.PtNo_Num = DV.PtNo_Num
WHERE Dsch_Date BETWEEN #SD AND #ED
) C
GROUP BY C.PT_NO, C.MED_REC_NO, C.PT_NAME
ORDER BY C.Pt_No;
I suspect the distinct in the subquery may not be necessary, but that depends on what your data really looks like.
UPDATE:
MSDN PIVOT
MSDN FUNCTION , TECHNET FUNCTION
MSDN APPLY - check cross apply statement
You defenitly need to look into pivot as #Darren Kopp says
Create table to map your values in IN clauses with groups
Then do pivot
Then simpify your case when to minimum(val, 6) using something like
CREATE FUNCTION Minimum
(#Param1 Integer, #Param2 Integer)
Returns Table As
Return(Select Case When #Param1 < #Param2
Then #Param1 Else #Param2 End MinValue)
So your table cdmap would be
6 | '196.0'
6 | '196.1'
6 | '196.2'
SELECT ...., [1], [2], [4], [6]
FROM
(
FROM smsdss.BMH_PLM_PtAcct_V PAV
JOIN smsdss.BMH_PLM_PtAcct_Clasf_Dx_V DV
ON PAV.PtNo_Num = DV.PtNo_Num
JOIN cdmap c on c.ClasfCd = dv.ClasfCd
WHERE Dsch_Date BETWEEN #SD AND #ED
) AS SourceTable
PIVOT
(
...
FOR c.ClasfCd IN ([1], [2], [4], [6])
) AS PivotTable;

Fetch data in a single Row

I have two tables PackageDetail and PackageDuration
PackageDuration have the PackageID as Foreign Key i.e. can have multiple records with respect to PackageID
The Schema of the PackageDetail is:
PackageID INT PK
PackageName Nvarchar(50)
Schema of the PackageDuration Table is:
DurationID INT PK
Price Money
Duration Nvarchar(50)
PackageID INT FPK
PackageDetail tables have follwoing records:
PackageID PackageName
1 TestPackage
2 MySecondPackage
PackageDuration table have following records:
DurationID PackageID Price Duration
1 1 100 6
2 1 200 12
3 1 300 24
4 2 500 6
PackageDuration table can have max 3 records with one PackageID not more than this(if have ignore that)
Now I want to select the Records as in following Way:
PackageId PackageNAme Price1 Price2 Price3 Duration1 Duration2 Duration3
1 TestPackage 100 200 300 6 12 24
2 MySecondPackage 500 null null 6 null null
Please suggest me how can I achive this.
Another approach:
WITH Durations AS
(
SELECT *, ROW_NUMBER() OVER(PARTITION BY PackageId ORDER BY DurationId) Sequence
FROM PackageDuration
)
SELECT A.PackageId, B.PackageName,
MIN(CASE WHEN Sequence = 1 THEN Price ELSE NULL END) Price1,
MIN(CASE WHEN Sequence = 2 THEN Price ELSE NULL END) Price2,
MIN(CASE WHEN Sequence = 3 THEN Price ELSE NULL END) Price3,
MIN(CASE WHEN Sequence = 1 THEN Duration ELSE NULL END) Duration1,
MIN(CASE WHEN Sequence = 2 THEN Duration ELSE NULL END) Duration2,
MIN(CASE WHEN Sequence = 3 THEN Duration ELSE NULL END) Duration3
FROM Durations A
INNER JOIN PackageDetail B
ON A.PackageId = B.PackageId
GROUP BY A.PackageId, B.PackageName
This should work as long as the durations are unique for a package and they are either 6, 12, or 24.
SELECT
PackageDetail.PackageId, PackageDetail.PackageName,
D1.Price as Price1, D2.Price as Price2, D3.Price as Price3,
D1.Duration as Duration1, D2.Duration as Duration2, D3.Duration as Duration3
FROM PackageDetail
LEFT OUTER JOIN PackageDuration D1
ON D1.PackageId = PackageDetail.PackageId AND D1.Duration = 6
LEFT OUTER JOIN PackageDuration D2
ON D2.PackageId = PackageDetail.PackageId AND D2.Duration = 12
LEFT OUTER JOIN PackageDuration D3
ON D3.PackageId = PackageDetail.PackageId AND D3.Duration = 24
;WITH pvt AS
(
SELECT PackageID,
Price1 = MAX(CASE WHEN Duration = 6 THEN Price END),
Price2 = MAX(CASE WHEN Duration = 12 THEN Price END),
Price3 = MAX(CASE WHEN Duration = 24 THEN Price END),
Duration1 = MAX(CASE WHEN Duration = 6 THEN 6 END),
Duration2 = MAX(CASE WHEN Duration = 12 THEN 12 END),
Duration3 = MAX(CASE WHEN Duration = 24 THEN 24 END)
FROM dbo.PackageDuration
GROUP BY PackageID
)
SELECT
pvt.PackageID,
p.PackageName,
pvt.Price1, pvt.Price2, pvt.Price3,
pvt.Duration1, pvt.Duration2, pvt.Duration3
FROM
dbo.PackageDetail AS p
INNER JOIN
pvt ON p.PackageID = pvt.PackageID
ORDER BY p.PackageID;
Maybe I'm missing something in the requirements, but it seems like Sql Server's PIVOT is what you're looking for.
There are quite a few questions here at SO about PIVOT... Here's a good clean example with references to other questions: How do i transform rows into columns in sql server 2005
The big benefit of a pivot table over the other answers here is that it will scale out with no modifications if you add records to your PackageDuration table in the future.