SQL move a duplicate value to a new column - sql

I found a similar problem here: Move duplicated values into unique columns, but I'm not sure that is going to work for me.
The data provided looks like this:
email_address barcode value
1#aol.com 9X3BZBK9CPWVDT16 25
123#verizon.net 9X3L6RD1Y83J8Z6Q 50
123#verizon.net 9X3PWCQRNX2CNJBC 50
2#naver.com 9X3N438B6TZYRD1N 50
3#msn.com 9X3PVFJ2N3C2DNW6 25
456#yahoo.com 9X3BCJ3YXFQ1RFGU 50
456#yahoo.com 9X3L1DVG89CGDBB0 25
But I would like to have it look like this:
EMAIL_ADDRESS BURNED_BARCODE1 value1 BURNED_BARCODE2 value2
1#aol.com 9X3BZBK9CPWVDT16 25
123#verizon.net 9X3L6RD1Y83J8Z6Q 50 9X3PWCQRNX2CNJBC 50
2#naver.com 9X3N438B6TZYRD1N 50
3#msn.com 9X3PVFJ2N3C2DNW6 25
456#yahoo.com 9X3BCJ3YXFQ1RFGU 50 9X3L1DVG89CGDBB0 25
I am trying to consolidate the duplicate coupon codes and values into a BARCODE_2 and value2. I have thought about pivot tables but i cannot seem to get the logic to work.
Any thoughts? Thank you in advance!

You could use the ROW_NUMBER() function for this and a self-join:
;WITH cte AS (SELECT *, ROW_NUMBER()OVER(PARTITION BY email_address ORDER BY barcode) AS RN
FROM Table1)
SELECT a.email_address, a.barcode, a.value, b.barcode AS barcode2, b.value AS value2
FROM cte a
LEFT JOIN cte b
ON a.email_address = b.email_address
AND a.RN = b.RN -1
WHERE a.RN = 1
Demo: SQL Fiddle
Update: You could add a 3rd:
;WITH cte AS (SELECT *, ROW_NUMBER()OVER(PARTITION BY email_address ORDER BY barcode) AS RN
FROM Table1)
SELECT a.email_address, a.barcode, a.value
, b.barcode AS barcode2, b.value AS value2
, c.barcode AS barcode3, c.value AS value3
FROM cte a
LEFT JOIN cte b
ON a.email_address = b.email_address
AND a.RN = b.RN -1
LEFT JOIN cte c
ON a.email_address = c.email_address
AND a.RN = c.RN -2
WHERE a.RN = 1
Demo: SQL Fiddle
If going beyond 2 a PIVOT answer may be preferable.

Related

Select MAX() or Select TOP 1 on Join

I'm working with the following code to only get one associated person per case, using the MAX Associated Type to get the top 1.
Associated Type is not a GUID, rather looks like:
Responsible Party, Primary Physician, etc.
It just so happens that Responsible Party is the last alphabetical option, so it's a lucky workaround. Not every case has a responsible party, however, and if there isn't a responsible party, the next top associated person is 'good enough' and will be highlighted as a data error anyway.
The result shows every single associated person (rather than top 1), but shows all of them as Responsible Party, which is not true. What am I doing wrong here?
FROM T_LatestIFSP Ltst
LEFT OUTER JOIN (
SELECT
Clas.ClientCase_ID,
MAX(Astp.AssociatedType) AS AssociatedType
FROM
T_ClientAssociatedPerson Clas
Inner Join T_AssociatedType Astp
ON Clas.AssociatedType_ID = Astp.AssociatedType_ID
GROUP BY Clas.ClientCase_ID
) AS Astp ON Ltst.ClientCase_ID = Astp.ClientCase_ID
LEFT OUTER JOIN T_ClientAssociatedPerson Clas
on Clas.ClientCase_ID = Astp.ClientCase_ID
LEFT OUTER JOIN T_AssociatedPerson Aspr
ON Aspr.AssociatedPerson_ID = Clas.AssociatedPerson_ID
To get AssocId in the select, you have to do a self join.
LEFT OUTER JOIN
(your subselect with max(AssociatedType) in it) AS Astp
INNER JOIN T_AssociatedType AS Astp2
ON (whatever the primary key is on that table)
Then you can add astp2.AssociationTypeId to the original SELECT.
You can try this query.
Make rn from your order condition in CASE WHEN
You can use Rank with window function to make rank number in subquery, then get rnk=1 data row.
;WITH CTE AS (
SELECT ClientCase_ID,
AssociatedPerson_ID,
AssociatedPersonType,
AssociatedType_ID,
RANK() OVER(PARTITION BY ClientCase_ID ORDER BY rn desc,AssociatedPerson_ID) rnk
FROM (
SELECT t1.ClientCase_ID,
t1.AssociatedPerson_ID,
t1.AssociatedPersonType,
t1.AssociatedType_ID,
(CASE
WHEN t1.AssociatedPersonType = 'ResPonsible Party' then 16
WHEN t1.AssociatedPersonType = 'Primary Physician' then 15
ELSE 14
END) rn
FROM T t1
INNER JOIN T t2 ON t1.ClientCase_ID = t2.AssociatedPerson_ID
UNION ALL
SELECT t2.AssociatedPerson_ID,
t1.AssociatedPerson_ID,
t1.AssociatedPersonType,
t2.AssociatedType_ID,
(CASE
WHEN t2.AssociatedPersonType = 'ResPonsible Party' then 16
WHEN t2.AssociatedPersonType = 'Primary Physician' then 15
ELSE 14
END) rn
FROM T t1
INNER JOIN T t2 ON t1.ClientCase_ID = t2.AssociatedPerson_ID
) t1
)
select DISTINCT ClientCase_ID,AssociatedPerson_ID,AssociatedPersonType,AssociatedType_ID
FROM CTE
WHERE rnk = 1
sqlfiddle
Also, you can try to use CROSS APPLY with value instead of UNION ALL
;with CTE AS (
SELECT v.*, (CASE
WHEN v.AssociatedPersonType = 'ResPonsible Party' then 16
WHEN v.AssociatedPersonType = 'Primary Physician' then 15
ELSE 14
END) rn
FROM T t1
INNER JOIN T t2 ON t1.ClientCase_ID = t2.AssociatedPerson_ID
CROSS APPLY (VALUES
(t1.ClientCase_ID,t1.AssociatedPerson_ID,t1.AssociatedPersonType, t1.AssociatedType_ID),
(t2.AssociatedPerson_ID,t1.AssociatedPerson_ID,t2.AssociatedPersonType, t2.AssociatedType_ID)
) v (ClientCase_ID,AssociatedPerson_ID,AssociatedPersonType,AssociatedType_ID)
)
SELECT distinct ClientCase_ID,AssociatedPerson_ID,AssociatedPersonType,AssociatedType_ID
FROM
(
SELECT *,
RANK() OVER(PARTITION BY ClientCase_ID ORDER BY rn desc,AssociatedPerson_ID) rnk
FROM CTE
) t1
WHERE rnk = 1
sqlfiddle
Note
you can add your customer order number in CASE WHEN
[Results]:
| ClientCase_ID | AssociatedPerson_ID | AssociatedPersonType | AssociatedType_ID |
|---------------|---------------------|----------------------|-------------------|
| 01 | 01 | ResPonsible Party | 16 |
| 02 | 03 | Physician Therapist | 24 |
I solved the problem with the following code:
LEFT OUTER JOIN T_ClientAssociatedPerson Clas
on Clas.ClientCase_ID = Ltst.ClientCase_ID
and
CASE
WHEN Clas.AssociatedType_ID = 16 AND Clas.ClientCase_ID = Ltst.ClientCase_ID THEN 1
WHEN Clas.AssociatedType_ID <> 16 AND Clas.AssociatedType_ID = (
SELECT TOP 1 Clas.AssociatedType_ID
FROM T_ClientAssociatedPerson Clas
WHERE Clas.ClientCase_ID = Ltst.ClientCase_ID
ORDER BY AssociatedType_ID DESC
) THEN 1
ELSE 0
END = 1

Get Incremental index for specific rows

I want to get the incremental index when note exists for the row. I am trying to achieve the same with ROW_Number() but it seems there is a problem with the method being used to generate it.
SELECT * RowNo,
(SELECT CASE
WHEN LEN(Value) > 0 THEN ROW_NUMBER()
OVER (
ORDER BY ID)
ELSE ''
END
FROM Dictionary
WHERE ID = ABC.ID) Note
FROM ABCD AS ABC WITH(NOLOCK)
INNER JOIN XYZ AS XYZ WITH(NOLOCK)
ON ABC.Id = XYZ.ID
WHERE ABC.Id = 10
output expected:
ID Name Note
1 A 1
2 B
3 C 2
4 D
5 E
6 F 3
The subquery isn't needed here, and you want to use the partition by argument to separate values having len(value)>0 from those having no value:
SELECT
ID,
Name,
CASE WHEN LEN(Value)>0 THEN ROW_NUMBER() OVER (
PARTITION BY CASE WHEN LEN(Value)>0 THEN 1 ELSE 0 END
ORDER BY ID) ELSE '' END as Note
FROM ABCD AS ABC WITH(NOLOCK)
INNER JOIN XYZ AS XYZ WITH(NOLOCK)
ON ABC.Id = XYZ.ID
Where ABC.Id = 10
I think maybe you need to change the approach to make the Dictionary query the "main" query. It's hard to say without knowing exactly what your tables look like. Which Table does the "Id" in your expected output come from?
Try like this:
WITH cte AS (
SELECT ID, ROW_NUMBER() OVER (ORDER BY ID) AS Note
FROM Dictionary WHERE ID=10
AND LEN(Value)>0
)
SELECT ABC.ID, [Name], cte.Note
FROM ABCD AS ABC WITH(NOLOCK)
INNER JOIN XYZ AS XYZ WITH(NOLOCK) ON ABC.Id = XYZ.ID
LEFT OUTER JOIN cte ON ABC.Id=cte.ID

Remove repeated values from the table

I have a PL/SQL select query like,
select
a.sgm,
b.numbr
from tbl1 a, tbl2 b
where b.itemId = a.itemId
and b.orgId = a.orgId
and a.srvCode = 'F'
and a.nbrCode <> 1
and rownum <= 7
Right now it retrieves like ,
sgm-|-numbr
-----------
abc-|-123
abc-|-678
abc-|-78
abc-|-099
bcd-|-153
bcd-|-123
bcd-|-123
I need to retrieve like ,
sgm-|-numbr
-----------
abc-|-123
bcd-|-153
ie, I need to remove the repeated ones in the first column. ie sgm shouldn't repeat.
Since you are using Oracle, then try this simplified version using a CTE:
WITH CTE as (
SELECT sgm, numbr,
rownum rn
FROM YourTable
)
SELECT CTE.sgm, CTE.numbr
FROM CTE
JOIN (
SELECT sgm, MIN(rownum) minrn
FROM CTE
GROUP BY sgm) t ON CTE.sgm = t.sgm AND CTE.rn = t.minrn
http://sqlfiddle.com/#!4/8d6fb/10
You can replace your query in the CTE above.
Good luck.
SELECT a.sgm, MAX(b.numbr)
FROM tbl1 a INNER JOIN tbl2 b
ON a.itemID = b.itemId
AND a.orgId = b.orgId
WHERE a.srvCode= 'F'
AND a.nbrCode <> 1
AND rownum <= 7
GROUP BY a.sgm
Apply a group function of your choice like MAX() on b.numbr, and apply the grouping on a.sgm, this should do what you need.
Advice : do your joins explicitly, see the difference between your query and mine.
select a.sgm,MAX(b.numbr)
from tbl1 a, tbl2 b
where b.itemId = a.itemId
AND b.orgId= a.orgId
and a.srvCode= 'F'
and a.nbrCode <> 1
and rownum<=7
group by sgm
The value of sgm wont repeat, but maximum value of number will be selected, similarly you can also select the minimum value using the Min function
Use group by function
select
a.sgm,
b.numbr
from tbl1 a, tbl2 b
where b.itemId = a.itemId
and b.orgId = a.orgId
and a.srvCode = 'F'
and a.nbrCode <> 1
and rownum <= 7
group by a.sgm
Select a from tbl a , tbl b WHERE a.userid > b..userid and
a.sgm = b.sgm;
Check this fiddle http://sqlfiddle.com/#!2/40b8f/2

Selecting max value from 2nd table in first table results

I have 2 tables as below-
Table I
ID DATE
1 05/11/12
2 23/11/12
3 29/11/12
4 04/10/12
5 20/11/12
And another table (IH) with the following info-
ID RECNO NOTE
1 1 Open
1 2 Update
1 3 Close
2 1 Open
2 2 Update
2 3 Hold
2 4 Close
3 1 Open
4 1 Open
4 2 Update
5 1 Open
I would like to output a result as shown below, displaying the Note field using the highest value of RecNo for each ID. So using the data above the output should be-
ID DATE NOTE
2 23/11/12 Close
3 29/11/12 Open
The code I have is-
SELECT I.ID, I.DATE, IH.NOTE FROM
I I, IH IH
JOIN (SELECT MAX([RECNO]) [RECNO] FROM
IH
GROUP BY RECNO) IH2 ON IH2.ID = IH.ID AND
IH2.[RECNO] = IH.[RECNO]
JOIN I I2 ON I2.ID = IH.ID WHERE
(I2.DATE>={TS ‘2012-11-22 00:00:002}) GROUP BY I2.ID
However when I execute the code I get-
Invalid Column Name 'RECNO'. Statement(s) could not be prepared.
How about this? Note, haven't tried it, I'm on my Mac at the moment.
SELECT I.ID, I.DATE, IH.NOTE
FROM I I
OUTER APPLY
(SELECT TOP 1 *
FROM IH
WHERE IH.ID = I.ID
ORDER BY RECNO DESC) IH
WHERE I.DATE >= '2012-11-22'
Your SQL is rather, uh, messy.
Assuming you are using SQL Server 2005 or greater, you can use the row_number() function, as follows:
SELECT I.ID, I.DATE, IH.NOTE
FROM I join
(select ih.*, ROW_NUMBER() over (PARTITION by id order by recno desc) as seqnum
from IH
) ih
on IH2.[RECNO] = IH.[RECNO] and seqnum = 1
WHERE I2.DATE>='2012-11-22 00:00:002'
This is assigning a sequence number in the IH table, for each id with the highest record number getting the value "1". The rest is just SQL.
Your original query is simply not correct syntactically, but I think this is what you want based on the description.
and another one
SELECT I.ID, I.DATE
,(Select TOP 1 IH.NOTE FROM IH where IH.ID=i.ID Order by Recno DESC) as Note
from I
WHERE
I.DATE>'20121122'
maybe this will help
SELECT a.ID, a.DATE, b.NOTE FROM a
inner join b on a.ID = b.ID
where b.recno in (select max(bb.recno)
from b as bb where bb.id = b.id)
http://sqlfiddle.com/#!3/fd141/2
If you don't mind the different identifiers, look at this solution:
select t1.MyID, t1.MyDate, y.Note
from t1
join
(
select MyID, max(RecNo) as RecNo
from t2
group by MyID
) x
on t1.MyID = x.MyID
left join
(
select *
from t2
) y
on t1.MyID = y.MyID
and x.RecNo = y.RecNo
where t1.MyDate >= '2012.11.22'
The complete solution is here: http://sqlfiddle.com/#!3/4ca09/3
Update: Oops, forgot to bring in the date in where clause. Updated SQL Fiddle and the query above.

Compare last to second last record for each contract

To keep it simple, my question is similar to THIS QUESTION, PART 2, only problem is, I am not running Oracle and thus can not use the rownumbers.
For those who need more information and examples:
I have a table
contractId date value
1 09/02/2011 A
1 13/02/2011 C
2 02/02/2011 D
2 08/02/2011 A
2 12/02/2011 C
3 22/01/2011 C
3 30/01/2011 B
3 12/02/2011 D
3 21/01/2011 A
EDIT: added another line for ContractID. Since I had some code myself, but that would display the following:
contractId date value value_old
1 09/02/2011 A
2 08/02/2011 A D
3 30/01/2011 B C
3 30/01/2011 B A
But that is not what I want ! The result should still be as below!
Now I want to select the last record before a given date and compare that with the previous value.
Suppose the 'given date' is 11/02/2011 in this example, the output should be like this:
contractId date value value_old
1 09/02/2011 A
2 08/02/2011 A D
3 30/01/2011 B C
I do have the query to select the last record before the given date. That is the easy part. But to select the last record before that, I am lost...
I really hope I can get some help here, have been breaking my head over this for days and looking for answers on the web and stackoverflow.
One possibility:
SELECT a.contractId, a.Date, a.Value, (SELECT Top 1 b.[Value]
FROM tbl b
WHERE b.[Date] < a.[Date] And b.ContractID=a.ContractID
ORDER BY b.[Date] Desc) AS Old_Value
FROM tbl AS a
WHERE a.Date IN
(SELECT TOP 1 b.Date
FROM tbl b
WHERE b.ContractID=a.ContractID
AND b.Date < #2011/02/11#
ORDER BY b.date DESC)
As promised, I would also post my answer. Although at this point, I still think Remou's answer is better, since the code is shorter and it seems more efficient (calls the same table fewer times). But here goes:
Query1:
SELECT c.contractID, c.firstofdates, a.value, d.value, d.date
FROM (table1 AS A RIGHT JOIN (SELECT b.cid,max(b.date) AS FirstOfdates
FROM table1 as B
where b.date < #02/11/2011#
GROUP BY b.contractID ) AS c ON (a.date = c.firstofdates) AND (a.contractID = c.contractID))
LEFT JOIN (select e.contractID, e.date, e.value
from table1 as e
) AS d ON (d.date < c.firstofdates) AND (d.contractID = c.contractID);
This query actually gives the result with the extra row for the 3rd contractID.
Query2:
SELECT b.contractID, max(a.date) AS olddate
FROM table1 AS a RIGHT JOIN (select contractID, firstofdates
from Query1) AS b ON (a.contractID= b.contractID) AND (a.date < b.firstofdates)
GROUP BY b.contractID;
And then to combine both:
Query3:
SELECT Query1.contractID, Query1.firstofdates AS [date], Query1.A.value AS [value], Query1.d.value AS [old value]
FROM Query1 RIGHT JOIN Query2
ON (Query1.date=Query2.olddate or Query2.olddate is null) AND (Query1.cid = Query2.cid);