Selecting only one row if the same ID - SQL Server - sql

I'm trying to learn SQL commands and working currently with an query which will list all customers which has status active (ID = 1) and active-busy (ID = 2).
The problem is that some customers have the same ID but the different type. So I have an customer which has ID 1 and Type 3 but the same customer has also ID 1 but Type 1 so what I'm trying to do is select only this which has Type 1 but have also the same ID. So IF ID is the same and Type is 1 and 3, select only Type 3.
SELECT
CASE
WHEN corel.opts LIKE 3
THEN (SELECT corel.opts
WHERE corel.objid = rel.id
AND corel.type IN (1, 2)
AND corel.opts = 3
ELSE corel.opts 1
END)
It's not complete query because it has many other this which I can't post but if you guys would show me way how could I accomplish that, I would appreciate it. I just don't know how to tell IF the same ID in the table but different Type - select only Type 3. Each customer have different ID but it can have the same type.

USE Row_number() like this
DECLARE #T TABLE
(
Id INT,
TypeNo INT
)
INSERT INTO #T
VALUES(1,1),(1,3),(2,1),(2,3),(3,1),(4,3)
;WITH CTE
AS
(
SELECT
RN = ROW_NUMBER() OVER(PARTITION BY Id ORDER BY TypeNo DESC),
Id,
TypeNo
FROM #T
)
SELECT
Id,
TypeNo
FROM CTE
WHERE RN = 1
My Input
Output

Test scenario is borrowed form Jayasurya Satheesh, thx, voted your's up!
DECLARE #T TABLE
(
Id INT,
TypeNo INT
)
INSERT INTO #T
VALUES(1,1),(1,3),(2,1),(2,3),(3,1),(4,3)
--The query will use ROW_NUMBER with PARTITION BY to start a row count for each T.Id separately.
--SELECT TOP 1 WITH TIES will take all first place rows and not just the very first:
SELECT TOP 1 WITH TIES
Id,
TypeNo
FROM #T AS T
ORDER BY ROW_NUMBER() OVER(PARTITION BY T.Id ORDER BY T.TypeNo DESC)
If your Type=3 is not the highest type code the simple ORDER BY T.TypeNo DESC won't be enough, but you can easily use a CASE to solve this.

As far as I understand, you need something like:
SELECT c1.*
FROM corel c1
LEFT OUTER JOIN corel c2 ON c1.objid=c2.objid AND c1.type <> c2.type
WHERE (c1.type=1 AND c2.type IS NULL) OR (c1.type=3 AND c2.type=1)

Related

SQL search to get last value in every unique single/group entry with same UserID? Entries can occure at different location in the dataset

This is my first post.
I have a table with lets say 3 fields (Id, UserID, Amount) and some data in it. Here is a sample table with some data in it.
My problem is that I need to get UserID and its last amount for every appearance like this. It can be several UserID in one table, amount is increasing with one, but sometimes two. I'm using Sqlite.
Try using LEAD()
FIDDLE DEMO
SELECT ID, UserID, AMOUNT FROM
(
SELECT ID, UserID, Amount,
CASE WHEN LEAD(UserID) OVER(ORDER BY ID) IS NULL THEN 0 ELSE LEAD(UserID) OVER(ORDER BY ID) END RN
FROM TABLE1
) X WHERE UserID <> RN OR RN = 0
This will give you the correct answer. Although, it is not the answer you expected as per my comment below your question.
select
a.ID,
a.UserID,
b.Amount
from
(
select
UserID,
max(ID) as ID
from
YOUR_TABLE
group by
UserID
) a
inner join YOUR_TABLE b on a.ID = b.ID and a.UserID = b.UserID
We can achieved it by creating a group id (changeinorder[col])
base on the ordering of UserID by detecting if there is a changes from current to previous rec
SELECT *,0 as changeinorder,LAG(Userid)
OVER (ORDER BY ID) AS 'Previous Record' INTO #TestB FROM #test
Output1
DECLARE #a INT
SELECT #a = 0
UPDATE #testB SET Changeinorder =#a, #a=
CASE WHEN Userid=[Previous Record]
THEN #a ELSE #a+1 END
Output2
SELECT MAX(ID),UseriD,MAX(Amount) FROM #testB GROUP BY UseriD,changeinorder
Final Output

How to find and update the first row of duplicated rows with SQL

I'm looking for a good way to do the following:
We have a table Accounts where some records have accidentally been imported twice, so they are duplicates. I have found that I can select all rows which are imported twice with the following query:
select name, vatnumber from Accounts
WHERE IsDeleted='false'
GROUP BY name, vatnumber
HAVING count(*) > 1
Basically: if the name and the vatnumber are the same, they are duplicates.
This gives me all the records that have been imported twice. Now I'm looking for a way to get the first Id of every double record so I can do something like:
UPDATE Accounts SET IsDeleted='true'
WHERE Id = (select id ...)
So basically, I'm trying to update the first row of every double record, so there are no more doubles. Can anyone please point me in the right direction? I haven't got the faintest clue on how to start doing this besides doing manual labour and I'm guessing there is a much easier way.
Sample data:
Id VatNumber Name
1 BE10128292 Microsoft
2 BE99292200 Google
3 BE10128292 Microsoft
4 BE99292200 Some other company
5 BE99292200 Google
Desired result:
Id VatNumber Name
1 BE10128292 Microsoft
2 BE99292200 Google
It doesn't matter whether I get the first Microsoft or the last Microsoft record really. Ideally, it would be the first though.
This should do the job, run the sample and check the output. It Updates your IsDeleted flag where your duplicates are found but uses the MIN(ID) to target the first row only.
CREATE TABLE #dupes
(
id INT,
vatNo NVARCHAR(20),
name NVARCHAR(20),
isDeleted BIT
DEFAULT 0
);
INSERT INTO #dupes
(
id,
vatNo,
name
)
VALUES
(1, 'BE10128292', 'Microsoft'),
(2, 'BE99292200', 'Google'),
(3, 'BE10128292', 'Microsoft'),
(4, 'BE99292200', 'Some other company'),
(5, 'BE99292200', 'Google');
UPDATE #dupes
SET isDeleted = 1
WHERE id IN (
SELECT MIN(id) MinId
FROM #dupes
WHERE isDeleted = 0
GROUP BY name,
vatNo
HAVING COUNT(*) > 1
);
SELECT *
FROM #dupes AS d;
DROP TABLE #dupes;
Produces:
id vatNo name isDeleted
1 BE10128292 Microsoft 1
2 BE99292200 Google 1
3 BE10128292 Microsoft 0
4 BE99292200 Some other company 0
5 BE99292200 Google 0
So specific to your database, the query would be:
UPDATE Accounts
SET isDeleted = 1
WHERE Id IN (
SELECT MIN(id) MinId
FROM Accounts
WHERE isDeleted = 0
GROUP BY name,
vatNo
HAVING COUNT(*) > 1
);
Try below using CTE :
;WITH cte
AS (
SELECT *,
ROW_NUMBER() OVER(PARTITION BY vatnumber ORDER BY id) rn
FROM <tablename>)
SELECT Id,
VatNumber,
Name
FROM cte
WHERE rn = 1;
Desired Result :
Id VatNumber Name
1 BE10128292 Microsoft
2 BE99292200 Google
Check This.
with CTE as
(
select *,ROW_NUMBER()over(partition by vatnumber,name order by ID )rowid
from #tableName
)
select * from CTE where rowid='2' // here you can change RowID 2 or 1
Check Demo Here
Try CTE as below :
WITH CTE
AS (
SELECT vatnumber,
name,
ROW_NUMBER() OVER(PARTITION BY vatnumber,
name ORDER BY ID) rowid,
IsDeleted
FROM Accounts
WHERE IsDeleted = 'false')
UPDATE CTE
SET
IsDeleted = 'true'
WHERE rowid > 1;
Try this,
;WITH removeDup as
(
SELECT *,ROW_NUMBER() OVER(PARTITION BY vatnumber,name ORDER BY ID ) DupId
from Accounts
)
DELETE from removeDup where DupId=2

SQL: How can I select top 1 row, based on different criteria on same column

Say I have a table which has a column named ItemCode, it has fixed format xxx-xxxx where x is [0-9], for example a possible value of ItemCode is 097-1234
Now I would like to select the largest ItemCode which starts with 987 AND the last ItemCode starts with 123, so I am trying to do something like (This is wrong)
SELECT TOP 1 ItemCode From Table
WHERE ItemCode like '987%' OR ItemCode like '123%'
ORDER BY 1 DESC
So how can I write a SQL which can select the last ItemCode of each criteria? Is there any general method which can extend to select top N rows on M such criterias?
(assuming there exists data fulfills the criteria, here 2 rows should be returned: largest ItemCode starts with 987 and largest ItemCode starts with 123)
Another option without UNION, you could use TOP 1 WITH TIES and ROW_NUMBER() OVER() like this
SELECT TOP 1 WITH TIES *
From YourTable
WHERE ItemCode like '987%' OR ItemCode like '123%'
ORDER BY ROW_NUMBER() OVER(PARTITION BY LEFT(ItemCode,3) ORDER BY Itemcode DESC)
You can use ROW_NUMBER() in a CTE for a more generalised form:
;With Ordered as (
select
*,
ROW_NUMBER() OVER (
PARTITION BY SUBSTRING(ItemCode,1,3)
ORDER BY ItemCode desc) as rn
from
Table
where
ItemCode like '987%' or
ItemCode like '123%'
)
select *
from Ordered
where rn = 1
As I alluded to in the comments, if possible I'd change the structure so that the ItemCode elements are separately stored, which would make for a simpler internal query form that could also more easily benefit from indexes. E.g. something like:
;With Ordered as (
select
*,
ROW_NUMBER() OVER (
PARTITION BY ItemCode_Prefix
ORDER BY ItemCode_Suffix desc) as rn
from
Table
where
ItemCode_Prefix in (987,123)
)
select *
from Ordered
where rn = 1
Thanks to #jarlh, I used UNION to achieve what I need.
If anyone has a more general method which may easier to be extended to more criteria, please post an answer and I will accept it. Cheers.
SELECT * FROM(
SELECT TOP 1 ItemCode FROM Table
WHERE ItemCode LIKE '987%'
ORDER BY 1 DESC
) AS A
UNION
SELECT * FROM(
SELECT TOP 1 ItemCode FROM Table
WHERE ItemCode LIKE '123%'
ORDER BY 1 DESC
) AS B
Complete solution based on ROW_NUMBER() function:
use tempdb;
go
-- Test data
create table #test_data
(ItemCode char(8) not null);
insert into #test_data
values
('097-1234'),
('097-1243'),
('097-7890'),
('012-1234'),
('912-1234'),
('123-1234'), -- second max for '987,123'
('123-1234'),
('123-0001'),
('123-0932'),
('987-1234'),
('987-5643'),
('987-7890'), -- first max for '987,123'
('000-7890');
go
-- Test data
-- Code
create proc dbo.top_n_from_m
#criterias varchar(max)
as
set nocount on;
declare #crs table
(id int not null identity (1, 1) primary key,
string char(3) not null);
insert into #crs (string)
select value
from string_split(#criterias, ',')
select t.ItemCode
from
(select t.ItemCode,
c.id,
row_id = row_number() over (partition by c.id order by t.ItemCode desc)
from #test_data as t
join #crs as c on t.ItemCode like c.string + '-%') as t
where t.row_id = 1
order by t.id
go
-- Code
-- Test
execute dbo.top_n_from_m #criterias = '987,123'
select ItemCode
from #test_data
order by ItemCode
-- Test
-- Clear
drop table #test_data;
drop proc dbo.top_n_from_m;
-- Clear

SQL Server sequence

I want to create a sequence group by a particular column.
I have a table LoanMaster and there is a column called BranchCode. It has branches 1 - 48. I want to generate a sequence number per branch. Like for branch code 1, if it has 10 records then generate a sequence number from 1 - 10, then for branch code 2 if it has 15 records generate number from 1 - 15 and so on.
How is it possible?
Use ROW_NUMBER:
SELECT
BranchCode,
[rn] = ROW_NUMBER() OVER(PARTITION BY BranchCode ORDER BY BranchCode)
FROM LoanMaster
ORDER BY BranchCode, rn
#EDIT:#
To start from arbitrary value just add it:
SqlFiddleDemo
SELECT
BranchCode,
[rn] = ROW_NUMBER() OVER(PARTITION BY BranchCode ORDER BY BranchCode) + 100
FROM LoanMaster
ORDER BY BranchCode, rn
#EDIT 2:#
Demo2
CREATE TABLE #LoanMaster(Id INT, LeadsID1 INT, LBrCode INT);
INSERT INTO #LoanMaster(Id, LBrCode)
VALUES (1,1), (2,1), (3,1), (4,1), (5,2), (6,2), (7,2);
;WITH cte AS
(
SELECT
Id,
LBrCode,
[RowNumber] = ROW_NUMBER() OVER(PARTITION BY LBrCode ORDER BY LBrCode) + 2405
FROM #LoanMaster
)
UPDATE T1
SET LeadsID1=c.RowNumber
FROM #LoanMaster AS T1
JOIN cte c
ON c.LBrCode=T1.LBrCode
AND c.Id = T1.Id
WHERE c.LBrCode=1;
SELECT *
FROM #LoanMaster;
This is my last update (accept answer or not) because initial question was answered long ago, for future:
you should ask specific question
provide actual table structures + SQLFiddle
provide desired output
specify what you want to achieve query/update
specify edge cases
Writing in comments more and more demands is not how SO works.

Updating field based on sorting of another

I have a user table which contains among others money, level and ranking.
Id | money| ranking| level
---------------------------
1 |30000| 1 1
2 |20000| 2 3
3 |10000| 3 2
4 |50000| 4 2
I want to update the ranking field based on user level (first filter) and money.
That is a user in higher level will always be ranked higher.
That is i want the table after the update like this:
Id | money| ranking| level
---------------------------
1 |30000| 4 1
2 |20000| 1 3
3 |10000| 3 2
4 |50000| 2 2
Thanks!
As a side note, I would NOT store this field within the database - storing values that are dependent on other records in the table make maintenance much more difficult.
Here's a query that would work as a view or within a stored procedure:
SELECT
ID,
[money],
ROW_NUMBER() OVER (order by [level] desc, [money] desc) AS [ranking],
[level]
FROM myTable
If you REALLY wanted to update the table just make the query a subquery to an update:
UPDATE m1
SET ranking = m2.ranking
FROM myTable m1
INNER JOIN
(SELECT
ID,
ROW_NUMBER() OVER (order by [level] desc, [money] desc) ranking
FROM myTable) m2
ON m1.ID = m2.ID
If you simply want to select then here is the query :
select *, dense_rank() over (order by level desc, mony desc) as newranking from YourTable
and if you want to update then :
;with cte_toupdate (ranking, newranking)
as (
select ranking, dense_rank() over (order by level desc, mony desc) as newranking from YourTable
)
Update cte_toupdate set ranking = newranking
select * from YourTable
check here : http://sqlfiddle.com/#!3/8d6d3/10
Note : if you want unique ranks then use Row_Number() instead of dense_rank().
CREATE TABLE #tt
(
id INT,
mony INT,
ranking INT,
levell INT
)
INSERT INTO #tt
VALUES (1,30000,1,1),
(2,20000,2,3),
(3,10000,3,2),
(4,50000,4,2)
UPDATE a
SET ranking = rnk
FROM #tt a
JOIN (SELECT Row_number()
OVER (
ORDER BY levell DESC, mony DESC) AS rnk,
*
FROM #tt) b
ON a.levell = b.levell
AND a.mony = b.mony