SQL Server sequence - sql

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.

Related

Removing All but the first and last values by group when the group is repeated in MS SQL Server (contiguous)

We have a chat system that generates multiple event logs per second sometimes for every event during a chat. The issue is that these consume a massive amount of data storage (which is very expensive on that platform) and we'd like to streamline what we actually store and delete things that really aren't necessary.
To that end, there's an event type for what position in the queue the chat is. We don't care about each position as long as they are not intervening events for that chat. So we want to keep only the first and last in each distinct group where there were no other event types to just get "total time in queue" for that period.
To complicate this, a customer can go in and out of queue as they get transferred by department, so the SAME CHAT can have multiple blocks of these queue position records. I've tried using FIRST_VALUE and LAST_VALUE and it gets me most of the way there, but fails when we have the case of two distinct blocks of these events.
Here's the script to generate the test data:
<!-- language: lang-sql -->
CREATE TABLE #testdata (
id varchar(18),
name varchar(8),
[type] varchar(20),
livechattranscriptid varchar(18),
groupid varchar(40))
INSERT INTO #testdata (id,name,[type],livechattranscriptid,groupid) VALUES
('0DZ14000003I2pOGAS','34128314','ChatRequest','57014000000ltfIAAQ','57014000000ltfIAAQChatRequest'),
('0DZ14000003IGmQGAW','34181980','Enqueue','57014000000ltfIAAQ','57014000000ltfIAAQEnqueue'),
('0DZ14000003IHbqGAG','34185171','Enqueue','57014000000ltfIAAQ','57014000000ltfIAAQEnqueue'),
('0DZ14000003ILuHGAW','34201743','Enqueue','57014000000ltfIAAQ','57014000000ltfIAAQEnqueue'),
('0DZ14000003IQ6cGAG','34217778','Enqueue','57014000000ltfIAAQ','57014000000ltfIAAQEnqueue'),
('0DZ14000003IR7JGAW','34221794','PushAssignment','57014000000ltfIAAQ','57014000000ltfIAAQPushAssignment'),
('0DZ14000003IiDnGAK','34287448','Enqueue','57014000000ltfIAAQ','57014000000ltfIAAQEnqueue'),
('0DZ14000003IiDoGAK','34287545','PushAssignment','57014000000ltfIAAQ','57014000000ltfIAAQPushAssignment'),
('0DZ14000003Iut5GAC','34336044','Enqueue','57014000000ltfIAAQ','57014000000ltfIAAQEnqueue'),
('0DZ14000003Iv7HGAS','34336906','Accept','57014000000ltfIAAQ','57014000000ltfIAAQAccept')
And here is the attempt to identify anything that was the first and last id for it's group ordered by the name field and grouped by the transcriptid:
select *,FIRST_VALUE(id) OVER(Partition BY groupid order by livechattranscriptid,name asc) as firstinstancegroup,
LAST_VALUE(id) OVER(Partition BY groupid order by livechattranscriptid,name asc RANGE BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING) as lastinstancegroup from #testdata order by livechattranscriptid,name
The issue is, it gives me the same first and last id for ALL of them by that entire group rather than treating each group of Enqueue records as a distinct group. How would I treat each distinct grouping instance of Enqueue as a unique group?
Here's a similar solution Grouping contiguous table data
not pretty but you will find the logic based from the OP. contiguous data over the same column
declare #mytable table (
id varchar(18),
name varchar(8),
[type] varchar(20),
livechattranscriptid varchar(18),
groupid varchar(100))
INSERT INTO #mytable (id,name,[type],livechattranscriptid,groupid) VALUES
('0DZ14000003I2pOGAS','34128314','ChatRequest','57014000000ltfIAAQ','57014000000ltfIAAQChatRequest'),
('0DZ14000003IGmQGAW','34181980','Enqueue','57014000000ltfIAAQ','57014000000ltfIAAQEnqueue'),
('0DZ14000003IHbqGAG','34185171','Enqueue','57014000000ltfIAAQ','57014000000ltfIAAQEnqueue'),
('0DZ14000003ILuHGAW','34201743','Enqueue','57014000000ltfIAAQ','57014000000ltfIAAQEnqueue'),
('0DZ14000003IQ6cGAG','34217778','Enqueue','57014000000ltfIAAQ','57014000000ltfIAAQEnqueue'),
('0DZ14000003IR7JGAW','34221794','PushAssignment','57014000000ltfIAAQ','57014000000ltfIAAQPushAssignment'),
('0DZ14000003IiDnGAK','34287448','Enqueue','57014000000ltfIAAQ','57014000000ltfIAAQEnqueue'),
('0DZ14000003IiDoGAK','34287545','PushAssignment','57014000000ltfIAAQ','57014000000ltfIAAQPushAssignment'),
('0DZ14000003Iut5GAC','34336044','Enqueue','57014000000ltfIAAQ','57014000000ltfIAAQEnqueue'),
('0DZ14000003Iv7HGAS','34336906','Accept','57014000000ltfIAAQ','57014000000ltfIAAQAccept')
;with myend as ( --- get all ends
select
*
from
(select
iif(groupid <> lead(groupid,1,groupid) over (order by name),
id,
'x') [newid],name
from #mytable
)x
where newid <> 'x'
)
, mystart as -- get all starts
(
select
*
from
(select
iif(groupid <> lag(groupid,1,groupid) over (order by name),
id,
'x') [newid], name,type,livechattranscriptid
from #mytable
)x
where newid <> 'x'
) ,
finalstart as ( --- get all starts including the first row
select id,
name,type,livechattranscriptid,
row_number() over (order by name) rn
from (
select id,name,type,livechattranscriptid
from (
select top 1 id, name,type,livechattranscriptid
from #mytable
order by name) x
union all
select newid,name,type,livechattranscriptid from mystart
) y
),
finalend as -- get all ends and add the last row
(
select id,
row_number() over (order by name) rn
from (
select id,name from (
select top 1 id,name
from #mytable
order by name desc) x
union all
select newid,name from myend
) y
)
select
s.id [startid]
,s.name
,s.type
,s.livechattranscriptid
,e.id [lastid]
from
finalend e
inner join finalstart s
on e.rn = s.rn --- bind the two results over the positions or row number

Selecting only one row if the same ID - SQL Server

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)

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: INSERT INTO SELECT MAX

I want to insert multi rows from another table. The problem is that I want to get the Max + 1 before I insert. Note that I know I should use Identify etc... However, I have this complex scenario of offline database synchronization across nodes...
INSERT INTO Purchase_Deliveries_Items
(ID,Item_ID)
SELECT
(SELECT
MAX(ID)+1 -- same MAX ID for all (the problem)
FROM
Purchase_Deliveries_Items),
Item_ID,
FROM
Purchase_Orders_Items
WHERE
PurchaseOrder_ID = 1
You can get a newid based on the max existing id with the help of ROW_NUMBER
INSERT INTO Purchase_Deliveries_Items (
ID,
Item_ID
)
SELECT
ROW_NUMBER() OVER (
ORDER BY
Item_ID
) + (SELECT MAX(ID)
FROM Purchase_Deliveries_Items) newID,
Item_ID,
FROM Purchase_Orders_Items
WHERE
PurchaseOrder_ID = 1
Seems like you want to do a kind of sequence, use a ROW_NUMBER:
INSERT INTO Purchase_Deliveries_Items
(ID,Item_ID)
SELECT (
SELECT MAX(ID) -- same MAX ID for all
FROM Purchase_Deliveries_Items
) + ROW_NUMBER() OVER (ORDER BY any_column),
Item_ID,
FROM Purchase_Orders_Items
WHERE PurchaseOrder_ID = 1

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