TSQL pivot issue - sql-server

Hello I have a temp table (#tempResult) that contains results like the following...
-----------------------------------------
| DrugAliasID | Dosage1 | Unit1 | rowID |
-----------------------------------------
| 322 | 10 | MG | 1 |
| 322 | 50 | ML | 2 |
| 441 | 20 | ML | 3 |
| 443 | 15 | ML | 4 |
-----------------------------------------
I'm looking to get the results to be like the following, pivoting the rows that have the same DrugAliasID.
--------------------------------------------------
| DrugAliasID | Dosage1 | Unit1 | Dosage2 | Unit2 |
--------------------------------------------------
| 322 | 10 | MG | 50 | ML |
| 441 | 20 | ML | NULL | NULL |
| 443 | 15 | ML | NULL | NULL |
--------------------------------------------------
So far I have a solution that isn't using pivot. I'm not too good with pivot and was wondering if anyone knew how to use it in this scenario. Or solve it some other way. Thanks
SELECT
tr.drugAliasID,
MIN(trmin.dosage1) AS dosage1,
MIN(trmin.unit1) AS unit1,
MIN(trmax.dosage1) AS dosage2,
MIN(trmax.unit1) AS unit2
FROM
#tempResult tr
JOIN
#tempResult trmin ON trmin.RowID = tr.rowid AND trmin.drugAliasID = tr.drugAliasID
JOIN
#tempResult trmax ON trmax.RowID = tr.rowid AND trmax.drugAliasID = tr.drugAliasID
JOIN
(SELECT
MIN(RowID) AS rowid,
drugAliasID
FROM
#tempResult
GROUP BY
drugAliasID) tr1 ON tr1.rowid = trmin.RowID
JOIN
(SELECT
MAX(RowID) AS rowid,
drugAliasID
FROM
#tempResult
GROUP BY
drugAliasID) tr2 ON tr2.rowid = tr.RowID
GROUP BY
tr.drugAliasID
HAVING
count(tr.drugAliasID) > 1

Assuming your version of SQL Server supports the use of CTEs, you can simplify your query thus:
;with cte as
(select *, row_number() over (partition by drugaliasid order by rowid) rn
from #tempResult
)
select c.drugaliasid, c.dosage1, c.unit1, c2.dosage1 as dosage2, c2.unit1 as unit2
from cte c
left join cte c2 on c.drugaliasid = c2.drugaliasid and c.rn = 1 and c2.rn = 2
where c.rn = 1
Demo
This will give you the desired result, without having to use the pivot keyword.

Related

SQL Server UPDATE - GROUP BY - MAX

This is SQL Server 2016. I have the following data in only one table:
custID | prodID | title | titleCount | isMasterTitle
--------+--------+--------+-------------+-----------
266 | 191750 | prod01 | 1 | 0
266 | 191750 | prod02 | 4 | 0
266 | 191750 | prod03 | 25 | 0
300 | 20125 | prod04 | 3 | 0
300 | 20125 | prod05 | 15 | 0
I want to group by custID, prodID and title and update isMasterTitle field to 1 for every max() titleCount per group.
So, I want the following:
custID | prodID | title | titleCount | isMasterTitle
--------+--------+----------+------------+---------------
266 | 191750 | prod01 | 1 | 0
266 | 191750 | prod02 | 4 | 0
266 | 191750 | prod03 | 25 | 1
300 | 20125 | prod04 | 3 | 0
300 | 20125 | prod05 | 15 | 1
I'm trying the following:
UPDATE [dbo].[_Variations]
SET isMasterTitle = 1
FROM [dbo].[_Variations] v1
INNER JOIN (SELECT custID, prodID, MAX(titleCount) AS mtitleCount
FROM [_Variations]
GROUP BY custID,prodID) as v2 ON v1.custID = v2.custID and v1.prodID = v2.prodID and v1.titleCount = v2.mtitleCount
try the following:
;with cte
as
(
select isMasterTitle, ROW_NUMBER() over (partition by custID, prodID order by titleCount desc) rn
from #t
)
update cte
set isMasterTitle = 1
where rn = 1
select * from #t
Your given code also works fine.
Please find the db<>fiddle here.
I would recommend leveraging a powerful feature of SQL Server called the updateable common-table-expression.
You can build a cte that uses window functions to identify which row should be updated, and then directly update it; there is no need to join again the original table in the outer query. This makes the query both shorter and more efficient:
with cte as (
select
isMaster,
row_number() over(partition by custID, prodID order by titleCount desc) rn
from [dbo].[_Variations]
)
update cte set isMaster = 1 where rn = 1

Assign value into specific column using Pivot or Unpivot

I have a table with below structure:
+-------+-----------+--------+----------+--------+
| RefNo | TranType | Code | Remarks | Amount |
+-------+-----------+--------+----------+--------+
| 1 | BD | 400201 | abcc dfr | 200 |
| 1 | BD | 400202 | abcc dfr | 200 |
| 2 | BD | 400204 | defrt | 300 |
| 2 | BD | 400205 | defrt | 300 |
+-------+-----------+--------+----------+--------+
I need to transpose these values to the below format:
+-------+--------+--------+----------+----------+--------+
| RefNo | Code1 | Code2 | TranType | Remarks | Amount |
+-------+--------+--------+----------+----------+--------+
| 1 | 400201 | 400202 | BD | abcc dfr | 200 |
| 2 | 400204 | 400205 | BD | defrt | 300 |
+-------+--------+--------+----------+----------+--------+
You don't need to use PIVOT, you can do it using a simple query.
SELECT t1.refno,
t1.code AS Code1,
t2.code AS Code2,
t1.trantype,
t1.amount
FROM #table t1
INNER JOIN #table t2
ON t1.refno = t2.refno
AND T1.code < T2.code
Online Demo
You can try the following query.
;WITH Tab (RefNo,Code,TranType,Remarks,Amount,rowno)
AS
(SELECT T.RefNo
, T.Code
,TranType
,Remarks
,Amount
, RN = ROW_NUMBER() OVER (PARTITION BY T.RefNo ORDER BY T.Code )
FROM Table1 T)
SELECT RefNo,Code1 = MAX( CASE WHEN N.rowno=1 THEN N.Code ELSE 0 END ),
Code2 = MAX( CASE WHEN N.rowno=2 THEN N.Code ELSE 0 END ) ,
TranType,Remarks,Amount FROM Tab n
GROUP BY N.RefNo,TranType,Remarks,Amount

SQL Server: Returning rows with multiple and distinct values

I've been working on this issue for the last day and a half and just can't seem to find another question on here that works for my code.
I have a table here:
Table_D
Policynumber| EntryDate | BI_Limit | P remium
------------------------------------------------------
ABCD100001 | 5/1/16 | 15/30 | 919
ABCD100001 | 5/13/16 | 15/30 | 1008
ABCD100002 | 5/24/16 | 100/300 | 1380
ABCD100003 | 5/30/16 | 25/50 | 1452
ABCD100003 | 6/2/16 | 25/50 | 1372
ABCD100003 | 6/4/16 | 30/60 | 951
ABCD100004 | 6/11/16 | 100/300 | 1038
ABCD100005 | 6/22/16 | 100/300 | 1333
ABCD100005 | 7/2/16 | 50/100 | 1208
ABCD100006 | 7/10/16 | 250/500 | 1345
ABCD100007 | 7/18/16 | 15/30 | 996
in which I'm trying to extract rows in which a policynumber has multiple listings and a different BI_Limit. So the output should be:
Output
Policynumber | EntryDate | BI_Limit | Premium
---------------------------------------------------
ABCD100003 | 5/30/16 | 25/50 | 1452
ABCD100003 | 6/2/16 | 25/50 | 1372
ABCD100003 | 6/4/16 | 30/60 | 951
ABCD100005 | 6/22/16 | 100/300 | 1333
ABCD100005 | 7/2/16 | 50/100 | 1208
I'm storing Policynumber as VARCHAR(Max), EntryDate as DATE, BI_Limit as VARCHAR(Max), and Premium as INTEGER.
The code I've want to say should work would be something along the lines of:
SELECT * FROM Table_D
WHERE BI_Limit IN (
SELECT BI_Limit
FROM Table_D
GROUP BY BI_Limit
HAVING COUNT(DISTINCT BI_Limit)>1);
But this returns nothing for me. Can anyone help to show me what I'm doing wrong? Thank you.
You could also try exists
select a.*
from Table_D a
where
exists (
select 1
from Table_D b
where a.Policynumber = b.Policynumber
and a.BI_Limit <> b.BI_Limit
)
SELECT d.*
FROM ( -- find the policy number with multiple listing and diff BI_Limit
SELECT PolicyNumber
FROM TableD
GROUP BY PolicyNumber
HAVING count(*) > 1
AND MIN (BI_Limit) <> MAX (BI_Limit)
) m -- join back the Table_D to for other information
INNER JOIN Table_D d
ON m.PolicyNumber = d.PolicyNumber

Using groupBy to improve my Select (select count) query

Let's say we have this and want to see all Tasks, that havent been done yet and an additional column showing how many open Tasks there are left for this customer.
I have a table like this in my database:
+------------+--------------------------+-------+
| CustomerID | Task | Done |
+------------+--------------------------+-------+
| 1 | CleanRoom | False |
| 1 | Cleandishes | True |
| 1 | WashClothes | False |
| 2 | TakeDogsOut | True |
| 2 | PlayWithKids | True |
| 3 | HaveFunWithMrSamplesWife | True |
| 3 | CleanMrSamplesCar | False |
+------------+--------------------------+-------+
I need this as returned table:
+------------+-------------------+-------------+
| CustomerID | Task | DoneOverAll |
+------------+-------------------+-------------+
| 1 | CleanRoom | 2 |
| 1 | WashClothes | 2 |
| 3 | CleanMrSamplesCar | 1 |
+------------+-------------------+-------------+
Perfect return table would be like this, but I can do that myself when I have the one above:
About this a question; Doing this will probably be a String combination task. Should I do this on the Select statement, or would it be more advisable to do that in the final application on the client computer?
+------------+-------------------+-------------+
| CustomerID | Task | DoneOverAll |
+------------+-------------------+-------------+
| 1 | CleanRoom | 1/3 |
| 1 | WashClothes | 1/3 |
| 3 | CleanMrSamplesCar | 1/2 |
+------------+-------------------+-------------+
I know I could go like
SELECT
a.CustomerID,
a.Task,
(
Select count(*) from myTable where
customerID = a.CustomerID and
done = False
) as DoneOverAll
FROM myTable as a
WHERE Done = False
But I think that this is very ineffective, since it would execute a Select Count for each row in my table. Is there a way to achieve this with a JOIN using groupBy or something? I'm not into GroupBy commands yet.
Okay I should have tried first. Came up with the following;
Select count(*), CustomerID from myTable group by CustomerID
All I need to do now is to get this into a join.
Okay, got it. Sorry again for not trying first!
SELECT
a.CustomerID,
a.Task,
b.cnt
FROM myTable as a
LEFT JOIN (select count(*) AS cnt, CustomerID FROM myTable GROUP BY CustomerID) as b on a.CustomerID = B.CustomerID
WHERE Done = False
Question left;
Perfect return table would be like this, but I can do that myself when I have the one above:
About this a question; Doing this will probably be a String combination task. Should I do this on the Select statement, or would it be more advisable to do that in the final application on the client computer?
+------------+-------------------+-------------+
| CustomerID | Task | DoneOverAll |
+------------+-------------------+-------------+
| 1 | CleanRoom | 1/3 |
| 1 | WashClothes | 1/3 |
| 3 | CleanMrSamplesCar | 1/2 |
+------------+-------------------+-------------+
I'm not sure why Done = False, but this is your logic. :-)
Here's what I would do, without the LEFT JOIN.
SELECT
a.CustomerID,
a.Task,
SUM(CASE WHEN a.Done = 'False' THEN 1 ELSE 0 END) DoneOverAll,
SUM(Case WHEN a.Done = 'True' THEN 1 ELSE 0 END) NotDone
FROM myTable as a
Group By a.CustomerID, a.Task
Do calculate separately .
;with tempfalse as(
SELECT
a.CustomerID,
a.Task,
count(*) as DoneOverAll
FROM myTable as a
WHERE Done = False
group by a.CustomerID, a.Task
)
, temptrue (
SELECT
a.CustomerID,
a.Task,
count(*) as total
FROM myTable as a
group by a.CustomerID, a.Task
)
SELECT
a.CustomerID,
a.Task,
cast(NULLIF(DoneOverAll,0) as varchar (10) ) + '/' + cast(NULLIF(b.total,0) as varchar (10) )
from temptrue as a left join tempfalse b
on a.CustomerID =a.CustomerID and
a.Task = b.Task

Update All other Records Based on a single record

I have a table with a million records. I need to update some columns which are null based on the existing 'not null' records of a particular id based columns. I've tried with one query, it seems to be working fine but I don't have confidence in it that it will be able to update all those 1 million records exactly the way I need. I'm providing you some sample data how my table looks like.Any help will be appreciated
SELECT * INTO #TEST FROM (
SELECT 1 AS EMP_ID,10 AS DEPT_ID,15 AS ITEM_NBR ,NULL AS AMOUNT,NULL AS ITEM_NME
UNION ALL
SELECT 1,20,16,500,'ABCD'
UNION ALL
SELECT 1,30,17,NULL,NULL
UNION ALL
SELECT 2,10,15,1000,'XYZ'
UNION ALL
SELECT 2,30,16,NULL,NULL
UNION ALL
SELECT 2,40,17,NULL,NULL
) AS A
Sample data:
+--------+---------+----------+--------+----------+
| EMP_ID | DEPT_ID | ITEM_NBR | AMOUNT | ITEM_NME |
+--------+---------+----------+--------+----------+
| 1 | 10 | 15 | NULL | NULL |
| 1 | 20 | 16 | 500 | ABCD |
| 1 | 30 | 17 | NULL | NULL |
| 2 | 10 | 15 | 1000 | XYZ |
| 2 | 30 | 16 | NULL | NULL |
| 2 | 40 | 17 | NULL | NULL |
+--------+---------+----------+--------+----------+
Expected result:
+--------+---------+----------+--------+----------+
| EMP_ID | DEPT_ID | ITEM_NBR | AMOUNT | ITEM_NME |
+--------+---------+----------+--------+----------+
| 1 | 10 | 15 | 500 | ABCD |
| 1 | 20 | 16 | 500 | ABCD |
| 1 | 30 | 17 | 500 | ABCD |
| 2 | 10 | 15 | 1000 | XYZ |
| 2 | 30 | 16 | 1000 | XYZ |
| 2 | 40 | 17 | 1000 | XYZ |
+--------+---------+----------+--------+----------+
I tried this but I'm unable to conclude whether it is updating all the 1 million records properly.
SELECT * FROM #TEST T
inner JOIN #TEST T1 ON T1.EMP_ID=T.EMP_ID
WHERE T1.AMOUNT IS NOT NULL
UPDATE T SET AMOUNT=T1.AMOUNT
FROM #TEST T
inner JOIN #TEST T1 ON T1.EMP_ID=T.EMP_ID
WHERE T1.AMOUNT IS not NULL
I have used UPDATE using inner join
UPDATE T
SET T.AMOUNT = X.AMT,T.ITEM_NME=X.I_N
FROM #TEST T
JOIN
(SELECT EMP_ID,MAX(AMOUNT) AS AMT,MAX(ITEM_NME) AS I_N
FROM #TEST
GROUP BY EMP_ID) X ON X.EMP_ID = T.EMP_ID
SELECT * into #Test1
FROM #TEST
WHERE AMOUNT IS NOT NULL
For records validation run this query first
SELECT T.AMOUNT, T1.AMOUNT, T1.EMP_ID,T1.EMP_ID
FROM #TEST T
inner JOIN #TEST1 T1 ON T1.EMP_ID=T.EMP_ID
WHERE T.AMOUNT IS NULL
Begin Trans
UPDATE T
SET T.AMOUNT=T1.AMOUNT, T.ITEM_NME= = T1.ITEM_NME
FROM #TEST T
inner JOIN #TEST1 T1 ON T1.EMP_ID=T.EMP_ID
WHERE T.AMOUNT IS NULL
rollback
SELECT EMP_ID,MAX(AMOUNT) as AMOUNT MAX(ITEM_NAME) as ITEM_NAME
INTO #t
FROM #TEST
GROUP BY EMP_ID
UPDATE t SET t.AMOUNT = t1.AMOUNT, t.ITEM_NAME = t1.ITEM_NAME
FROM #TEST t INNER JOIN #t t1
ON t.emp_id = t1.emp_id
WHERE t.AMOUNT IS NULL and t.ITEM_NAME IS NULL
Use MAX aggregate function to get amount and item name for each employee and then replace null values of amount and item name with those values. For validation use COUNT function to calculate the number of rows with values of amount and item name as null. If the number of rows is zero then table is updated correctly

Resources