TSQL - Get Item not duplicate in group and in Max group - sql-server

I have Data Table Example:
Declare #tb1 Table(ItemId int , GroupDuplicateId int, GroupType nvarchar(10))
Insert Into #tb1 Values(1,1,'IN'),(1,1,'VN'),(1,2,'IN'),(1,3,'IN'),(1,2,'VN'),(1,3,'VN'),(1,3,'SK')
Insert Into #tb1 Values(2,1,'IN'),(2,1,'VN')
select * from #tb1
Order by ItemId, GroupDuplicateId, GroupType
Now, I want get Items in 1 one group .
Example: ItemId 1 have GroupDuplicateId: 1, 2 ,3 . We need one of thems.
ItemId 2 satistify because, only 1 group.
Declare #tbResult1 Table(ItemId int , GroupDuplicateId int, GroupType nvarchar(10))
Insert Into #tbResult1 Values(1,1,'IN'),(1,1,'VN')
Insert Into #tbResult1 Values(2,1,'IN'),(2,1,'VN')
select * from #tbResult1
--2. The same 1. But, we need Group 3. It's have 3 Group Type: IN, VN, SK.
Insert Into #tbResult2 Values(1,3,'IN'),(1,3,'VN'),(1,3,'SK') -- Have max in Group
Insert Into #tbResult2 Values(2,1,'IN'),(2,1,'VN')
select * from #tbResult2
Thanks for your help.

Is this what you are looking for .?
Declare #tb1 Table(ItemId int , GroupDuplicateId int, GroupType nvarchar(10))
Insert Into #tb1 Values(1,1,'IN'),(1,1,'VN'),(1,2,'IN'),(1,3,'IN'),(1,2,'VN'),(1,3,'VN'),(1,3,'SK')
Insert Into #tb1 Values(2,1,'IN'),(2,1,'VN')
/*
select * from #tb1
Order by ItemId, GroupDuplicateId, GroupType */
;with cte as (
select *,row_number()over(partition by itemid,GroupType order by itemid,GroupDuplicateId)rn from #tb1
)
select * from cte where rn=1

Related

How to show all columns with count?

In the result of my select I see 2 lines that's OK, but I want to see all columns like:
'Anne','Bauer','m1'
'Thomas','Neben','m3'
If (OBJECT_ID('tempdb..##test') Is Not Null)
Begin
Drop Table ##test
End
CREATE TABLE ##test (
givenname varchar(50),
surname varchar(50),
rann varchar(50)
);
INSERT INTO ##test VALUES ('Anne','Bauer','m1');
INSERT INTO ##test VALUES ('Klaus','Griebe','m2');
INSERT INTO ##test VALUES ('Thomas','Neben','m2');
INSERT INTO ##test VALUES ('Thomas','Neben','m3');
SELECT count(rann) as coun, rann
FROM ##test
group by rann
having count(rann) = 1
order by coun desc
You can try this:
select givenname,surname,rann from (
select *,count(*) over (partition by rann) rn from ##test
)t where rn=1
just use partition by with count
Select count(rann) over (partition by rann) [count], rann from ##test

What is the optimal way to get only latest ID's from table in SQL

I'm trying to get only a single row per Appointment Number in a table storing a history of appointments. It works fine with a few rows but then gets slower? Is this the best way to do this kind of check and I'm just missing some indexes or is there a better way?
DECLARE #temptable TABLE
(
id INT PRIMARY KEY NOT NULL
, ApptNumber INT NOT NULL
, ApptDate DATE NOT NULL
, Notes VARCHAR(50) NULL
)
INSERT INTO #temptable VALUES (1,1,'01-DEC-2018','First Appointment')
INSERT INTO #temptable VALUES (2,1,'01-DEC-2018','')
INSERT INTO #temptable VALUES (3,1,'01-DEC-2018','Rescheduled')
INSERT INTO #temptable VALUES (4,2,'02-DEC-2018','Second Appointment')
INSERT INTO #temptable VALUES (5,2,'02-DEC-2018','Cancelled')
INSERT INTO #temptable VALUES (6,3,'03-DEC-2018','Third Appointment')
INSERT INTO #temptable VALUES (7,4,'04-DEC-2018','Fourth Appointment')
SELECT * FROM #temptable
SELECT MAX(id) FROM #temptable GROUP BY ApptNumber
SELECT tt.* FROM #temptable tt
INNER JOIN (SELECT MAX(id) [Id] FROM #temptable GROUP BY ApptNumber) appts ON appts.Id = tt.id
Solution 1:
select * from (
SELECT f1.*, row_number() over(partition by ApptNumber order by id desc ) rang FROM #temptable f1
) tmp where rang=1
Solution 2:
with tmp as (
select ApptNumber, max(ID) MaxID
from #temptable
group by ApptNumber
)
select f1.* from #temptable f1 inner join tmp f2 on f1.ID=f2.MaxID
Solution 3:
select distinct f3.* from #temptable f1
cross apply
(
select top 1 * from #temptable f2
where f1.ApptNumber=f2.ApptNumber
order by f2.ID desc
) f3
Window function
SELECT tt.*
FROM (
SELECT *, row_number() over (partition by ApptNumber order by id desc) as rn
) tt
where tt.rn = 1

Filling the ID column of a table NOT using a cursor

Tables have been created and used without and ID column, but ID column is now needed. (classic)
I heard everything could be done without cursors. I just need every row to contain a different int value so I was looking for some kind of row number function :
How do I use ROW_NUMBER()?
I can't tell exactly how to use it even with these exemples.
UPDATE [TableA]
SET [id] = (select ROW_NUMBER() over (order by id) from [TableA])
Subquery returned more than 1 value.
So... yes of course it return more than one value. Then how to mix both update and row number to get that column filled ?
PS. I don't need a precise order, just unique values. I also wonder if ROW_NUMBER() is appropriate in this situation...
You can use a CTE for the update
Example
Declare #TableA table (ID int,SomeCol varchar(50))
Insert Into #TableA values
(null,'Dog')
,(null,'Cat')
,(null,'Monkey')
;with cte as (
Select *
,RN = Row_Number() over(Order by (Select null))
From #TableA
)
Update cte set ID=RN
Select * from #TableA
Updated Table
ID SomeCol
1 Dog
2 Cat
3 Monkey
You can use a subquery too as
Declare #TableA table (ID int,SomeCol varchar(50))
Insert Into #TableA values
(null,'Dog')
,(null,'Cat')
,(null,'Monkey');
UPDATE T1
SET T1.ID = T2.RN
FROM #TableA T1 JOIN
(
SELECT ROW_NUMBER()OVER(ORDER BY (SELECT 1)) RN,
*
FROM #TableA
) T2
ON T1.SomeCol = T2.SomeCol;
Select * from #TableA

How to write a SQL script that deletes duplicate posts

I have a table with these columns:
id (pk, int identity), imei (varchar), name (varchar), lastconnected (datetime)
Some of the entries in this table have the same name and imei, but different id and different lastconnected date.
How can I effectively filter out all entries that have duplicates (with a SQL script), and then delete the one with the latest lastconnected date?
A simple ROW_NUMBER and DELETE should do the trick:
WITH CTE AS
(
SELECT *,
RN = ROW_NUMBER() OVER(PARTITION BY imei, [name] ORDER BY lastconnected DESC)
FROM dbo.YourTable
)
DELETE FROM CTE
WHERE RN = 1;
This is easy and will solve your problem
DECLARE #table TABLE
(
id int,
name varchar(10),
imei varchar(10)
)
insert into #table select 1, 'a','a'
insert into #table select 2, 'b','a'
insert into #table select 3, 'c','a'
insert into #table select 4, 'a','a'
insert into #table select 5, 'c','a'
insert into #table select 6, 'a','a'
insert into #table select 7, 'c','a'
insert into #table select 8, 'a','a'
WHILE (exists (select '' from #table group by name , imei having count(*) > 1))
BEGIN
delete from #table where id in (
select max(id) from #table group by imei , name having count(*) > 1)
End
select * from #table
My first instinct is to use RANK(). This will delete all duplicates, not just the most recent, in cases where things are duplicated multiple times.
delete a
from (
select id, imei, name, lastconnected, RANK() over(partition by imei, name order by lastconnected) as [rank] from #temp
) as a
where a.rank>1
It selects the maximum of the date for each combination of name and iemi and then deletes that particular row.
DELETE FROM yourtablee
WHERE (lastconnecteddate,name,imei) in
(SELECT max(lastconnecteddate), name,imei
FROM yourtable
GROUP BY name,imei)

How to use Substring to pull multiple items from a field

First post - I am trying to pull ten different pieces of information from a single field. Let me start with this is not my table, just what I was given to work with. This is a varchar max field.
'3350|#|1234567|~|3351|#|8/1/2017|~|3352|#|Acme|~|3353|~|10000.00|~|3354|#||~|3355|#||~3356|#|Yes|~|3357|#|Doe,John|~|3358|#|CA|~|3359|#|5551212'
I know that the numbers that start with 33 are keys telling me what information is in that section. 3350 has the invoice #1234567. 3351 has the invoice date of 8/1/17. etc. 3354 and 3355 were left null. The keys are unchanging and will be the same for every record in the table.
I need to pull the data from between 3350|#| and |~|3351 to get my invoice# and between 3351|#| and |~|3352 to get my date, etc, but I am struggling with how to word this. Any help would be appreciated and any critiques on my first post will be taken constructively.
The #YourTable is just a table variable used for demonstration / illustration
For Rows - Example
Declare #YourTable table (ID int,SomeCol varchar(max))
Insert Into #YourTable values
(1,'3350|#|1234567|~|3351|#|8/1/2017|~|3352|#|Acme|~|3353|#|10000.00|~|3354|#||~|3355|#||~|3356|#|Yes|~|3357|#|Doe,John|~|3358|#|CA|~|3359|#|5551212')
Select A.ID
,Item = left(RetVal,charindex('|#|',RetVal+'|#|')-1)
,Value = right(RetVal,len(RetVal)-charindex('|#|',RetVal+'|#|')-2)
From #YourTable A
Cross Apply [dbo].[udf-Str-Parse](A.SomeCol,'|~|') B
Returns
For Columns - Example
Declare #YourTable table (ID int,SomeCol varchar(max))
Insert Into #YourTable values
(1,'3350|#|1234567|~|3351|#|8/1/2017|~|3352|#|Acme|~|3353|#|10000.00|~|3354|#||~|3355|#||~|3356|#|Yes|~|3357|#|Doe,John|~|3358|#|CA|~|3359|#|5551212')
Select *
From (
Select A.ID
,Item = left(RetVal,charindex('|#|',RetVal+'|#|')-1)
,Value = right(RetVal,len(RetVal)-charindex('|#|',RetVal+'|#|')-2)
From #YourTable A
Cross Apply [dbo].[udf-Str-Parse](A.SomeCol,'|~|') B
) A
Pivot (max([Value]) For [Item] in ([3350],[3351],[3352],[3353],[3354],[3355],[3356],[3357],[3358],[3359]) ) p
Returns
The UDF if Interested
CREATE FUNCTION [dbo].[udf-Str-Parse] (#String varchar(max),#Delimiter varchar(10))
Returns Table
As
Return (
Select RetSeq = Row_Number() over (Order By (Select null))
,RetVal = LTrim(RTrim(B.i.value('(./text())[1]', 'varchar(max)')))
From (Select x = Cast('<x>' + replace((Select replace(#String,#Delimiter,'§§Split§§') as [*] For XML Path('')),'§§Split§§','</x><x>')+'</x>' as xml).query('.')) as A
Cross Apply x.nodes('x') AS B(i)
);
--Thanks Shnugo for making this XML safe
--Select * from [dbo].[udf-Str-Parse]('Dog,Cat,House,Car',',')
--Select * from [dbo].[udf-Str-Parse]('John Cappelletti was here',' ')
You can try a tally based splitter like below
declare #t table ( id int, col nvarchar(max));
insert into #t values
(1, '3350|#|1234567|~|3351|#|8/1/2017|~|3352|#|Acme|~|3353|~|10000.00|~|3354|#||~|3355|#||~3356|#|Yes|~|3357|#|Doe,John|~|3358|#|CA|~|3359|#|5551212')
,(2, '3350|#|123334567|~|3351|#|8/2/2017|~|3352|#|Acme|~|3353|~|10000.00|~|3354|#||~|3355|#||~3356|#|Yes|~|3357|#|Doe,John|~|3358|#|CA|~|3359|#|5551212');
select
id,
case
when split_values like '3350|#|%' then 'id'
when split_values like '3351|#|%' then 'date'
end as fieldname,
SUBSTRING(split_values,8,LEN(split_values)-7) as value
from
(
select
--t.col as col,
row_number() over (partition by t.col order by t1.N asc) as row_num,
t.id,
SUBSTRING( t.col, t1.N, ISNULL(NULLIF(CHARINDEX('|~|',t.col,t1.N),0)-t1.N,8000)) as split_values
from #t t
join
(
select
t.col,
1 as N
from #t t
UNION ALL
select
t.col,
t1.N + 3 as N
from #t t
join
(
select
top 8000
row_number() over(order by (select NULL)) as N
from
sys.objects s1
cross join
sys.objects s2
) t1
on SUBSTRING(t.col,t1.N,3) = '|~|'
) t1
on t1.col=t.col
)a
where
split_values like '3350|#|%' or
split_values like '3351|#|%'
Live demo

Resources