SQL selecting from a particular duplicate row - sql

Hi all I have a sql query
SELECT FKid1, FKID2, a, b, c
from [source]
where FKID1 = 3
which returns the following data set so
(hope formatting holds)
FKID1 FKID2 A B C
3 40297 0 0 2
3 40297 0 100 1
3 40325 0 0 2
3 40325 0 0 3
3 40325 0 10 -1
3 40348 0 10 3
3 40391 0 10 -1
3 40392 0 10 -1
3 40501 0 10 -1
3 40501 0 0 2
I'm trying to improve this query so that if there are 2 rows with duplicate FKID1 and FKID2 values, it will pick the column B value from a particular row as follows...
if there is a row with C = -1 use the B value in this row and ignore others.
if there are 2 rows with C <> -1 then pick the MAX(B) value.
For rows that are not duplicated, return as normal.
IE the results should look as follows...
FKID1 FKID2 A B C
3 40297 0 100 1
3 40325 0 10 -1
3 40348 0 10 3
3 40391 0 10 -1
3 40392 0 10 -1
3 40501 0 10 -1
the correct values selected for the B column and no dupes.
We have a solution at the moment, but I think it's overcomplicated and wondered if anyone had any ideas?
Thanks.

One way
;WITH cte As
(
SELECT FKid1, FKID2, a, b, c,
ROW_NUMBER() OVER (PARTITION BY FKID1, FKID2
ORDER BY CASE WHEN C = -1 THEN 0 ELSE 1 END ASC, B DESC) AS RN
from [source]
where FKID1 = 3
)
SELECT FKid1, FKID2, a, b, c
FROM cte
WHERE RN=1

Select T1.FKID1, T1.FKID2
, Min(A) As A
, Case
When Count(*) = 2 And NegCB.B Is Not Null Then Max(NegCB.B)
Else Max(B)
End As B
, Min(C) As C
From Source As T1
Outer Apply (
Select T3.B
From Source As T3
Where T2.FKID1 = T1.FKID1
And T2.FKID2 = T1.FKID2
And T2.C = -1
) As NegCB
Where FKID1= 3
Group By T1.FKID1, T1.FKID2

Related

Slicing table data based on days

Having the following data table:
col1
days
A
2
B
3
C
1
C
5
D
3
A
3
B
7
A
4
I want to transform it into:
col1
<=2days
<=5days
<=7days
A
1
2
0
B
0
1
1
C
1
1
0
D
0
1
0
I used the below query to achieve this:
select col1,
CASE WHEN days <=2 then count(days) as "<=2days",
CASE WHEN days > 2 and days <=5 then count(days) as "<=5days",
CASE WHEN days > 5 and days <=7 then count(days) as "<=7days"
from tableA group by col1,days
But it returns a result like the following:
col1
<=2days
<=5days
<=7days
A
1
0
0
A
0
2
0
B
0
1
0
B
0
0
1
C
0
1
0
C
1
0
0
D
0
1
0
Can someone please help here?
You could use conditional aggregation as the following:
SELECT col1,
COUNT(CASE WHEN days<=2 THEN 1 END) AS '<=2days',
COUNT(CASE WHEN days<=5 and days>2 THEN 1 END) AS '<=5days',
COUNT(CASE WHEN days<=7 and days>5 THEN 1 END) AS '<=7days'
FROM tableA
GROUP BY col1
ORDER BY col1
See a demo on MySQL.

Select table adding columns with data depending on duplicates in other column

Imagine this data.
Id
Type
1
A
1
B
1
B
2
A
3
B
I want to select table and ad two columns turning it to this. How can i do it? (In teradata)
Id
Type
Id with both A+B
Id with only A
1
A
1
0
1
B
1
0
1
B
1
0
2
A
0
1
3
B
0
0
I'm not familiar with teradata but in standard SQL next query should be working:
SELECT
T.*,
CASE WHEN Cnt = 2 THEN 1 ELSE 0 END AS BOTH_TYPES_PRESENT,
CASE WHEN Cnt = 1 AND Type = 'A' THEN 1 ELSE 0 END AS ONLY_A_PRESENT
FROM T
LEFT JOIN (
SELECT Id, COUNT(DISTINCT Type) Cnt FROM T WHERE Type IN ('A', 'B') GROUP BY Id
) CNT ON T.Id = CNT.Id;
SQL online editor

Update a column if a row is not a duplicate, or a row's ID isn't a certain number in SQL SSMS

I have a table, and I would like the following update command to set isExcluded = 1 to all rows where PhoneID and PhoneName are not duplicates and all rows where the ID doesn't not have the smallest number from a selected PhoneID if those rows do not have a duplicate PhoneID and PhoneName (i.e.: since all rows with PhoneID = 2 are not duplicates, the row containing PhoneName = b has the smallest ID, since it's ID = 3. Therefore, all rows with PhoneID = 2 and ID > 3 will have their IsExcluded set to 1).
ID
PhoneID
PhoneName
isExcluded
1
1
a
0
2
1
a
0
3
2
b
0
4
2
c
0
5
2
d
0
6
2
e
0
7
3
c
0
8
3
c
0
9
3
d
0
10
3
d
0
Here's my SQL script that I wrote. It only seems to get the non-duplicates only.
WITH Duplicates AS
(
SELECT
ID, PhoneID, PhoneName, isExcluded,
(ROW_NUMBER() OVER (PARTITION BY PhoneName, PhoneID ORDER BY ID)) AS RowNum
FROM
Phones
)
UPDATE Phones
SET isExcluded = 1
FROM Duplicates d
WHERE (
d.PhoneID = Phones.PhoneID
AND d.PhoneName = Phones.PhoneName
AND d.RowNum =< 1);
SELECT * FROM Phones;
This table should be the result of my command.
ID
PhoneID
PhoneName
isExcluded
1
1
a
0
2
1
a
0
3
2
b
0
4
2
c
1
5
2
d
1
6
2
e
1
7
3
c
0
8
3
c
0
9
3
d
1
10
3
d
1
This looks to be a variation of a gaps and islands problem, which you can solve by first grouping the partitions and then using an updatable CTE to assign the isExcluded value
with gp as (
select *,
Row_Number() over(order by id)
- Row_Number() over(partition by phoneid, phonename order by id) gp
from t
), p as (
select *,
case when Min(gp) over(partition by phoneid) <gp then 1 end IsEx
from gp
)
update p set isExcluded = isEx
where IsEx = 1
See working DB<>Fiddle

Rank function gives incorrect Ranking

select distinct a.pattern ,convert(numeric(18,0) ,isnull(b.[DCount]as [DCount]
,DENSE_RANK () OVER ( Partition by a.pattern order by b.[DCount]desc ) as [Rank]
from a, b
Gives output as
Pattern Dcount Rank
A 0 1
A 0 1
A 0 2
A 0 2
B 0 2
B 0 2
B 0 2
B 0 2
C 0 2
C 0 2
C 0 2
Whereas required output is
Pattern Dcount Rank
A 0 1
A 0 1
A 0 1
A 0 1
B 0 1
B 0 1
B 0 1
B 0 1
C 0 1
C 0 1
C 0 1
Perhaps you just want the ranking by dcount. If so:
select distinct a.pattern, convert(numeric(18,0),
coalesce(b.DCount, 0) as DCount,
dense_rank() over (order by b.[DCount] desc) as [Rank]
from a cross join b;
use DENSE_RANK below way
select * ,DENSE_RANK () OVER ( order by Pattern, Dcount) as rn from t
Try this: you need to partition by Pattern,dcount and order by only pattern
demo
select *,dense_Rank() over(partition by Pattern,dcount order by Pattern) from tablename

Clause ORDER BY

I have a problem with a select in sql server, i have this table with 2 columns:
a 2
b 1
c 100
d 1
a 100
b 1
c 2
d 1
I want ordered it based on the first column,in this way:
a 2
a 100
b 1
b 1
c 2
c 100
d 1
d 1
But then j want the rows with secondcolumn=100 be moved at the bottom,so:
a 2
b 1
b 1
c 2
d 1
d 1
a 100
c 100
I have tried with clause ORDER BY column1 ASC, (column2=100) ASC,but it didnt work!
Thankyou and greetings.
Actually, you want the rows with 100 in the second column moved to the bottom first, and then ordered by the first column:
order by (case when col2 = 100 then 1 else 0 end),
col1
Use the CASE expression as below
SELECT *
FROM tab
ORDER BY CASE
WHEN column2 = 100 THEN 1
ELSE 0
END ASC,
column1 asc
SELECT *
FROM table1
ORDER BY
CASE
WHEN col2>=100 THEN 1
ELSE 0
END,
col1,
col2
SQLFiddle Example