Find the Missing Key ID or Numbers from a Column values - sql

Need to find the missing numbers which have been deleted or a Column does not have yet.
For example:
i have a table Named Person have Columns [PersonID] [PersonName]
[PersonID] is primary and incremented Number e.g. From 1 to N.
PersonID PersonName
1001 ABC
1002 ABC
1003 XYZ
1004 MNO
1006 ABC
1008 MNO
1009 ABC
1010 ABC
1011 XYZ
1014 ABC
1015 ABC
1016 XYZ
1017 MNO
In the given table ,there are some missing numbers in Column PersonID like
1005
1007
1012
1013
Need to find the missing Numbers only.
Note: There are more than 20 million records in my table.
So please suggest a faster method to find the desired numbers.

Thanks to all of you who supported and share some points. I have found the way to find the Missing using ROWNUMBER().
SELECT
NOTEXIST FROM (
SELECT ROW_NUMBER() OVER (ORDER BY PERSONID) NOTEXIST ,PERSONID FROM #A ) T
WHERE NOTEXIST NOT IN ( SELECT PERSONID FROM PERSONID )

Create another table and populate all the numbers between Min and Max ranges of PersonID. Do an anti join (Left/right) to get the list of numbers missing.
select * from NewIDTable a
left join OriginalTable b on a.PersonID=b.PersonID
where b.Personid is null

The simplest way is to get ranges. You can do this with lead():
select personid + 1, next_personid - 1 as end_range,
next_personid - personid - 1 as num_missing
from (select t.*,
lead(personid) over (order by personid) as next_personid
from t
) t
where next_personid <> personid + 1;
If you still want the list of ids, you can expand out the ranges, but that depends on the database.
In SQL Server 2008, this is much more performance intensive, but you can do it:
select personid + 1, tnext.personid - 1 as end_range,
text.personid - personid - 1 as num_missing
from t cross apply
(select top (1) t2.person_id
from t t2
where t2.personid > t.person_id
order by t2.personid asc
) tnext
where tnext.personid <> personid + 1;

Related

Get next value for each of values from next table CTE

I have the following table:
dbo.split
Name Time
Alex 120
John 80
John 300
Mary 500
Bob 900
And then another table dbo.travel
Name Time
Alex 150
Alex 160
Alex 170
John 90
John 100
John 310
Mary 550
Mary 600
Mary 499
Bob 800
Bob 700
For each value in table split I need to find the next value in table travel. I tried to do it with CTE a with ROW_NUMBER() to get next by group, but there's no way I can group by correct value, since dbo.split can containt multiple values for the same name.
I'm looking for the following output:
Name Time TravelTime
Alex 120 150
John 80 90
John 300 310
Mary 500 550
Bob 900 NULL
Here's what I have so far but it fails because split table can have multiple records per person:
;with result as (
select t.*,
ROW_NUMBER() OVER (Partition BY t.Name order by t.Time) as rn
from travel t join split s
on t.Name = s.Name and t.TIME>s.Time
)
I would use apply:
select s.*, t.time
from split s outer apply
(select top (1) t.*
from travel t
where t.name = s.name and t.time > s.time
order by t.time asc
) t;
In this case, apply is doing essentially the same thing as a correlated subquery, so you could phrase it that way as well.
You can try as below
Select * from(Select
Name,t.time,t1.time,
Row_number() over (partition by
Name,t.time order by t1.time) rn
from split t
Join travel t1 on t.time <t1.time and
t.name =t1.name)
where
rn=1;

countif type function in SQL where total count could be retrieved in other column

I have 36 columns in a table but one of the columns have data multiple times like below
ID Name Ref
abcd john doe 123
1234 martina 100
123x brittany 123
ab12 joe 101
and i want results like
ID Name Ref cnt
abcd john doe 123 2
1234 martina 100 1
123x brittany 123 2
ab12 joe 101 1
as 123 has appeared twice i want it to show 2 in cnt column and so on
select ID, Name, Ref, (select count(ID) from [table] where Ref = A.Ref)
from [table] A
Edit:
As mentioned in comments below, this approach may not be the most efficient in all cases, but should be sufficient on reasonably small tables.
In my testing:
a table of 5,460 records and 976 distinct 'Ref' values returned in less than 1 second.
a table of 600,831 records and 8,335 distinct 'Ref' values returned in 6 seconds.
a table of 845,218 records and 15,147 distinct 'Ref' values returned in 13 seconds.
You should provide SQL brand to know capabilities:
1) If your DB supports window functions:
Select
*,
count(*) over ( partition by ref ) as cnt
from your_table
2) If not:
Select
T.*, G.cnt
from
( select * from your_table ) T inner join
( select count(*) as cnt from your_table group by ref ) G
on T.ref = G.ref
You can use COUNT with OVERin following:
QUERY
select ID,
Name,
ref,
count(ref) over (partition by ref) cnt
from #t t
SAMPLE DATA
create table #t
(
ID NVARCHAR(400),
Name NVARCHAR(400),
Ref INT
)
insert into #t values
('abcd','john doe', 123),
('1234','martina', 100),
('123x','brittany', 123),
('ab12','joe', 101)

SQL - Looking to show when 2 columns combined have the same data

I have a database table that has a Vendor_ID column and a Vendor_Item column.
Vendor_id Vendor_item
101 111
101 111
101 123
I need a way to show when vendor_id and vendor_item are combined, show if having count greater than 1. The vendor_item number can be in there multiple times as long as it has a different vendor_id.
Vendor_id Vendor_item
101 111
101 111
I have done the following but it only shows results have have more than 1 and doesn't show both records like the above example.
SELECT vendor_id,vendor_item
From Inventory_master
group by vendor_id,vendor_item
having count(*) >1
If possible I would like a way to add another column ( UPC ) to the results. The system I am working on can import back into the system with UPC so I would be able to fix what is duplicated.
Vendor_id Vendor_item UPC
101 111 456
101 111 789
Not sure about the UPC column as from where and how you are getting it but you can change your existing query a bit like below to get the desired data
SELECT * FROM Inventory_master WHERE vendor_item IN (
SELECT vendor_item
From Inventory_master
group by vendor_item
having count(vendor_item) >1);
You can use a subquery and then JOIN back to the inventory_master table:
SELECT im.*
FROM
Inventory_master im INNER JOIN (
SELECT vendor_id, vendor_item
From Inventory_master
group by vendor_id,vendor_item
having count(*) >1) s
ON im.vendor_id = s.vendor_id AND im.vendor_item = s.vendor_item
Try this
select * from(
select vendor_id,vendor_item, count(*) over (partition by vendor_id) cnt
from Inventory_master
) where cnt>1

sql that identifies which account numbers have multiple agents

I dont think a count will work here, can someone help me get an sql that identifies which account numbers have multiple agents, more than two agents in the where condition.
AGENT_NAME ACCOUNT_NUMBER
Clemons, Tony 123
Cipollo, Michael 123
Jepsen, Sarah 567
Joanos, James 567
McMahon, Brian 890
Novak, Jason 437
Ralph, Melissa 197
Reitwiesner, John 221
Roman, Marlo 123
Rosenzweig, Marcie 890
Results should be something like this.
ACCOUNT_NUMBER AGENT_NAME
123 Cipollo, Michael
123 Roman, Marlo
123 Clemons, Tony
890 Rosenzweig, Marcie
890 McMahon, Brian
567 Joanos, James
567 Jepsen, Sarah
You can do this using window functions:
select t.account_number, t.agent_name
from (select t.*, min(agent_name) over (partition by account_number) as minan,
max(agent_name) over (partition by account_number) as maxan
from table t
) t
where minan <> maxan;
If you know the agent names are never duplicated, you could just do:
select t.account_number, t.agent_name
from (select t.*, count(*) over (partition by account_number) as cnt
from table t
) t
where cnt > 1;
Assuming your table name is test, this should pull all the records with duplicate ACCOUNT_NUMBER:
select * from test where ACCOUNT_NUMBER in
(select ACCOUNT_NUMBER from test
group by ACCOUNT_NUMBER having
count(ACCOUNT_NUMBER)>1)
order by ACCOUNT_NUMBER
Using count function u can get the result
CREATE TABLE #TEMP
(
AGENT_NAME VARCHAR(100),
ACCOUNT_NUMBER INT
)
INSERT INTO #TEMP
VALUES ('CLEMONS, TONY',123),
('CIPOLLO, MICHAEL',123),
('JEPSEN, SARAH',567),
('JOANOS, JAMES',567),
('MCMAHON, BRIAN',890),
('NOVAK, JASON',437),
('RALPH, MELISSA',197),
('REITWIESNER, JOHN',221),
('ROMAN, MARLO',123),
('ROSENZWEIG, MARCIE',890)
SELECT a.ACCOUNT_NUMBER,a.AGENT_NAME
FROM #TEMP A
JOIN(SELECT COUNT(1) CNT,
ACCOUNT_NUMBER
FROM #TEMP
GROUP BY ACCOUNT_NUMBER) B
ON A.ACCOUNT_NUMBER = B.ACCOUNT_NUMBER
WHERE B.CNT != 1

SQL Query for avoiding any repetition for a specific column terms

I am looking to design a query in which I need DISTINCT terms in a column without repetition. I am using the SQL Server 2008 R2 edition.
Here is my sample table:
id bank_code bank_name interest_rate
----------------------------------------------------------
1 123 abc 3.5
2 456 xyz 3.7
3 123 abc 3.4
4 789 pqr 3.3
5 123 abc 3.6
6 456 xyz 3.1
What I want is, to sort the table descending on the 'interest_rates' column but without any repetition of the terms in 'bank_code'.
Here is what I want:
id bank_code bank_name interest_rate
----------------------------------------------------------
2 456 xyz 3.7
5 123 abc 3.6
4 789 pqr 3.3
I have been trying the DISTINCT operator but it selects the unique combination of all the columns and not the single column for repetition.
Here is what I am doing, which clearly would not do get me what I want:
SELECT DISTINCT TOP 5 [ID], [BANK_CODE]
,[BANK_NAME]
,[INTEREST_RATE]
FROM [SAMPLE]
ORDER BY [INTEREST_RATE] DESC
Is there a way to achieve this?
Any help is appreciated.
;WITH x AS
(
SELECT id,bank_code,bank_name,interest_rate,
rn = ROW_NUMBER() OVER (PARTITION BY bank_code ORDER BY interest_rate DESC)
FROM dbo.[SAMPLE]
)
SELECT id,bank_code,bank_name,interest_rate
FROM x WHERE rn = 1
ORDER BY interest_rate DESC;
Try using analytical functions:
;WITH CTE AS
(
SELECT *, ROW_NUMBER() OVER(PARTITION BY bank_code ORDER BY interes_rate DESC) Corr
FROM [Sample]
)
SELECT id, bank_code, banck_name, interest_rate
FROM CTE
WHERE Corr = 1
not sure about the [] syntax, but you probably need something like this:
SELECT min([ID]), [BANK_CODE], [BANK_NAME], max([INTEREST_RATE])
FROM [SAMPLE]
GROUP BY [BANK_CODE], [BANK_NAME]
ORDER BY 4 DESC
How about something like this. It is simple, but will duplicate if you have interest rates that are the same.
select ID, #sample.Bank_code, bank_name, #sample.interest_Rate
from #sample
join
(
SELECT [BANK_CODE], MAX(interest_rate) as interest_Rate
FROM #sample
GROUP BY bank_code
) as groupingtable
on groupingtable.bank_code = #sample.bank_code
and groupingtable.interest_Rate = #sample.interest_rate