Return only single record if name is test - sql

I have a table:
Declare #t table (ID int,name nvarchar(100))
Insert into #t values (1,'Test')
Insert into #t values (1,'A')
Insert into #t values (1,'B')
Insert into #t values (2,'R')
Insert into #t values (2,'S')
Insert into #t values (3,'T')
My requirement is to return only 1 record for the id which is having 'Test' as name:
My output will return:
ID name
1 Test
2 R
2 S
3 T
I tried this query:
select * from #t t
where exists (select 1 from #t t1 where t.ID=t1.ID and name ='test')
But no luck.
Can anyone please tell me what is the issue here?

You can do it with NOT EXISTS:
SELECT t.*
FROM #t t
WHERE t.name = 'Test'
OR NOT EXISTS (SELECT 1 FROM #t WHERE ID = t.ID AND name = 'Test')
Or with MAX() window function:
SELECT ID, name
FROM (
SELECT *,
MAX(CASE WHEN name = 'Test' THEN name END) OVER (PARTITION BY ID) test
FROM #t
) t
WHERE test IS NULL OR name = test;
See the demo.
Results:
> ID | name
> -: | :---
> 1 | Test
> 2 | R
> 2 | S
> 3 | T

You can use the DENSE_RANK as follows:
select id,name from
(select id, name,
DENSE_RANK()
over(partition by id
order by case when t.id = 1 then 1 else 2 end) rn
from #t)t
where t.rn = 1

Related

Case when duplicate add one more letter

For example: I have a table with these records below
1 A
2 A
3 B
4 C
...
and I need to migrate these record in to another table
1 AA
2 AB
3 B
4 C
...
Meaning if the record is duplicate, it will automatically add one more letter alphabetically.
Just a slightly different approach
Example
Declare #YourTable Table (ID int,[SomeCol] varchar(50))
Insert Into #YourTable Values
(1,'A')
,(2,'A')
,(3,'B')
,(4,'C')
Select *
,NewVal = concat(SomeCol,IIF(sum(1) over (partition by SomeCol)=1,'',char(64+row_number() over ( partition by SomeCol order by ID ))) )
From #YourTable
Returns
ID SomeCol NewVal
1 A AA
2 A AB
3 B B
4 C C
EDIT - Requested UPDATE
Declare #YourTable Table (ID int,[SomeCol] varchar(50))
Insert Into #YourTable Values
(1,'A')
,(2,'A')
,(3,'B')
,(4,'C')
Select *
,NewVal = concat(SomeCol,IIF(sum(1) over (partition by SomeCol)=1,'',replace(char(63+row_number() over ( partition by SomeCol order by ID )),'#','')) )
From #YourTable
Returns
ID SomeCol NewVal
1 A A
2 A AA
3 B B
4 C C
We might be able to handle this requirement with the help of a calendar table mapping secondary letters to duplicate sequence counts:
WITH letters AS (
SELECT 1 AS seq, 'A' AS let UNION ALL
SELECT 2, 'B' UNION ALL
SELECT 3, 'C' UNION ALL
...
SELECT 26, 'Z' UNION ALL
...
),
cte AS (
SELECT id, let, ROW_NUMBER() OVER (PARTITION BY let ORDER BY id) rn,
COUNT(*) OVER (PARTITION BY let) cnt
FROM yourTable
)
SELECT t1.id, t1.let + CASE WHEN t1.cnt > 1 THEN t2.let ELSE '' END AS let
FROM cte t1
LEFT JOIN letters t2
ON t1.id = t2.seq
ORDER BY t1.id;
Demo

SQL count number of records where value remains constant

I need to find the count of tracker_id where position remains 1 through out the table.
tracker_id | position
---------------------
5 | 1
11 | 1
4 | 1
4 | 2
5 | 2
4 | 1
4 | 1
11 | 1
14 | 1
9 | 2
Here, the output should be 2 since, position of tracker_id:11 and 14 remains 1 through out the table.
You can use not exists
select count(*) from tbl a
where not exists(select 1
from tbl b
where a.tracker_id = b.tracker_id
and a.position <> b.position )
and a.position = 1
Output: 2
declare #table1 as table (tracker_id int,postion int)
insert into #table1 values (5,1)
insert into #table1 values (11,1)
insert into #table1 values (4,1)
insert into #table1 values (4,2)
insert into #table1 values (5,2)
insert into #table1 values (4,1)
insert into #table1 values (4,1)
insert into #table1 values (11,1)
insert into #table1 values (14,1)
insert into #table1 values (9,2)
select count(tracker_id),tracker_id,postion from #table1 group by tracker_id,postion
You can also do:
select ( count(distinct tracker_id) -
count(distinct tracker_id) filter (where position <> 1)
) as num_all_1s
from t;
Using uncorrelated subquery
select count(distinct tracker_id)
from t
where position=1
and tracker_id not in (select tracker_id from t where position<>1);
Using window function
select count(distinct tracker_id)
from (select *, avg(position) over (partition by tracker_id) as avg_pos from t) a
where avg_pos=1;
This one is just for giggles
select distinct count(*) over ()
from t
group by tracker_id
having count(*) = sum(position);
And if you really want to have fun
select count(distinct tracker_id)-count(distinct case when position<>1 then tracker_id end)
from t;
If position can only be 1, then you can use this, which gets all the tracker_ids with only a single position value, and then limits that to those records where position = 1:
WITH agg AS
(
SELECT
tracker_id
, p = MAX(position)
FROM table1
GROUP BY tracker_id
HAVING COUNT(DISTINCT position) = 1
)
SELECT COUNT(tracker_id)
FROM agg
WHERE p = 1

Is it possible to find (in an ordered table) multiple rows in sequence? [duplicate]

This question already has an answer here:
Compare Current Row with Previous/Next row in SQL Server
(1 answer)
Closed 4 years ago.
If I have a table ordered by ID like so:
|---------------------|------------------|
| ID | Key |
|---------------------|------------------|
| 1 | Foo |
|---------------------|------------------|
| 2 | Bar |
|---------------------|------------------|
| 3 | Test |
|---------------------|------------------|
| 4 | Test |
|---------------------|------------------|
Is there a way to detect two rows that match a where clause in sequence?
For example, in the table above, I would like to see if any two rows in succession have a Key of 'test'.
Is this possible in SQL?
Another option is a variation of Gaps-and-Islands
Example
Declare #YourTable Table ([ID] int,[Key] varchar(50))
Insert Into #YourTable Values
(1,'Foo')
,(2,'Bar')
,(3,'Test')
,(4,'Test')
Select ID_R1 = min(ID)
,ID_R2 = max(ID)
,[Key]
From (
Select *
,Grp = ID-Row_Number() over(Partition By [Key] Order by ID)
From #YourTable
) A
Group By [Key],Grp
Having count(*)>1
Returns
ID_R1 ID_R2 Key
3 4 Test
EDIT - Just in case the IDs are NOT Sequential
Select ID_R1 = min(ID)
,ID_R2 = max(ID)
,[Key]
From (
Select *
,Grp = Row_Number() over(Order by ID)
-Row_Number() over(Partition By [Key] Order by ID)
From #YourTable
) A
Group By [key],Grp
Having count(*)>1
You can try to use ROW_NUMBER window function check the gap.
SELECT [Key]
FROM (
SELECT *,ROW_NUMBER() OVER(ORDER BY ID) -
ROW_NUMBER() OVER(PARTITION BY [Key] ORDER BY ID) grp
FROM T
)t1
GROUP BY [Key]
HAVING COUNT(grp) = 2
You can do a self join as
CREATE TABLE T(
ID INT,
[Key] VARCHAR(45)
);
INSERT INTO T VALUES
(1, 'Foo'),
(2, 'Bar'),
(3, 'Test'),
(4, 'Test');
SELECT MIN(T1.ID) One,
MAX(T2.ID) Two,
T1.[Key] OnKey
FROM T T1 JOIN T T2
ON T1.[Key] = T2.[Key]
AND
T1.ID <> T2.ID
GROUP BY T1.[Key];
Or a CROSS JOIN as
SELECT MIN(T1.ID) One,
MAX(T2.ID) Two,
T1.[Key] OnKey
FROM T T1 CROSS JOIN T T2
WHERE T1.[Key] = T2.[Key]
AND
T1.ID <> T2.ID
GROUP BY T1.[Key]
Demo
You can use the LEAD() window function, as in:
with
x as (
select
id, [key],
lead(id) over(order by id) as next_id,
lead([key]) over(order by id) as next_key
from my_table
)
select id, next_id from x where [key] = 'test' and next_key = 'test'

TSQL Distinct Counts

I have a table that looks like this:
ID SuppressionTypeID PersonID
------------------------------
1 1 123
2 1 456
3 2 456
I want to get a rolling count (distinct people) rather than a normal group by count.
e.g. not this:
SuppressionTypeID Count
---------------------------
1 2
2 1
This:
SuppressionTypeID RecordsLost
----------------------------------
1 2
2 0
The latter being zero as we lost person 456 on suppresiontypeid 1.
Thanks in advance.
You may need to use a temporary table or a table variable as shown below
DECLARE #t TABLE (
ID INT
,SuppressionTypeID INT
,PersonID INT
)
INSERT INTO #t
SELECT 1
,1
,123
UNION ALL
SELECT 2
,1
,456
UNION ALL
SELECT 3
,2
,456
DECLARE #t1 TABLE (
ID INT
,SuppressionTypeID INT
,PersonID INT
,firstid INT
)
INSERT INTO #t1
SELECT *
,NULL
FROM #t
UPDATE t1
SET t1.firstid = t2.firstid
FROM #t1 AS t1
INNER JOIN (
SELECT personid
,min(SuppressionTypeID) AS firstid
FROM #t1
GROUP BY personid
) AS t2 ON t1.PersonID = t2.PersonID
SELECT coalesce(t2.firstid, t1.SuppressionTypeID) AS SuppressionTypeID
,count(DISTINCT t2.personid) AS count
FROM #t1 AS t1
LEFT JOIN #t1 AS t2 ON t1.personid = t2.personid
AND t1.SuppressionTypeID = t2.firstid
GROUP BY coalesce(t2.firstid, t1.SuppressionTypeID)
The result is
SuppressionTypeID count
----------------- -----------
1 2
2 0
You can try;
with tmp_tbl as (
select
x.SuppressionTypeID, count(x.PersonID) as RecordsLost
from (
select
min(SuppressionTypeID) as SuppressionTypeID,
PersonID
from tbl
group by PersonID
) as x
group by x.PersonID
order by x.SuppressionTypeID
)
select
distict t.SuppressionTypeID, coalesce(tmp.RecordsLost, 0) as RecordsLost
from tbl t
left join tmp_tbl tmp on tmp.SuppressionTypeID = t.SuppressionTypeID

SQL field = other field minus another row

Table has 2 cols: [nr] and [diff]
diff is empty (so far - need to fill)
nr has numbers:
1
2
45
677
43523452
on the diff column i need to add the differences between pairs
1 | 0
2 | 1
45 | 43
677 | 632
43523452 | 43522775
so basically something like :
update tbl set diff = #nr - #nrold where nr = #nr
but i don't want to use fetch next, because it's not cool, and it's slow (100.000) records
how can I do that with one update?
CREATE TABLE #T(nr INT,diff INT)
INSERT INTO #T (nr) SELECT 1
UNION SELECT 2
UNION SELECT 45
UNION SELECT 677
UNION SELECT 43523452
;WITH cte AS
(
SELECT nr,diff, ROW_NUMBER() OVER (ORDER BY nr) RN
FROM #T
)
UPDATE c1
SET diff = ISNULL(c1.nr - c2.nr,0)
FROM cte c1
LEFT OUTER JOIN cte c2 ON c2.RN+1= c1.RN
SELECT nr,diff FROM #T
DROP TABLE #T
Have a look at something like this (full example)
DECLARE #Table TABLE(
nr INT,
diff INT
)
INSERT INTO #Table (nr) SELECT 1 UNION ALL
SELECT 2 UNION ALL
SELECT 45 UNION ALL
SELECT 677 UNION ALL
SELECT 43523452
;WITH Vals AS (
SELECT nr,
diff,
ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) xID
FROM #Table
)
UPDATE c
SET diff = c.nr - ISNULL(p.nr, 0)
FROM Vals c LEFT JOIN
Vals p ON c.xID = p.xID + 1
SELECT *
FROM #Table
try this -
update tablename
set diff = cast(nr as INT) - cast((select nr from tablename where diff is not null and nr = a.nr) as INT)
from tablename a
where diff is null
This is assuming you only have one older row for nr old in the table. else the subquery will return more than one value