How to increment value when they are same - sql

I have a data
By using this query I'm getting data like this
Select ID,Val,Premium,Row_number()OVER(PARTITION BY ID,Val ORDER BY ID) RN From Table1
Present Data
ID Val Premium RN
1 CH 201 1
1 CH 0 1
1 CHH 301 2
1 CHS 401 3
How can I make this data to like on the present Query
ID Val Premium RN
1 CH 201 1
1 CH 0 4
1 CHH 301 2
1 CHS 401 3
I just want to make the data which is same RN to increment to maximum number
Val = CH have RN = 1 then I want to make Premium = 0 record to RN = 4
In the present Row number I need to pick MAX(RN) and increment to that duplicate value

This should do the trick:
UPDATE table1
SET rn = (SELECT Max(rn) + 1
FROM table1)
WHERE id IN (SELECT id
FROM table1
WHERE rn IN (SELECT rn
FROM table1
GROUP BY rn
HAVING Count(*) > 1))
AND premium = 0;
Update the table. Setting the new RN value to the highest value+1. Only updating the rows where there are multiple RN values and where premium is 0.

--Try This
BEGIN TRAN
Declare #Strt INT,#End INT,#MaxNo INT
CREATE TABLE #TEMP_DATA (ID INT, VAL NVARCHAR(6),Premium INT)
INSERT INTO #TEMP_DATA
SELECT 1,'CH',201 UNION ALL
SELECT 1,'CH',0 UNION ALL
SELECT 1,'CHH',301 UNION ALL
SELECT 1,'CHS',401 UNION ALL
SELECT 1,'CHHS',501 UNION ALL
SELECT 1,'CHHS',0
SELECT ID,Val,Premium,DENSE_RANK()OVER(ORDER BY Val) RN INTO #T
FROM #TEMP_DATA
SET #Strt=1
SELECT #End= MAX(RN) FROM #T
WHILE #Strt<=#End BEGIN
SELECT #MaxNo=MAX(RN)+1 FROM #T
UPDATE #T SET RN= #MaxNo WHERE Premium=0 AND RN=#Strt
SET #Strt=#Strt+1
END
SELECT * FROM #T
ROLLBACK TRAN

Related

How to arrange continuous serial number in to two or multiple column sequentially in sql server?

I want to print or display 1 to 10 or any max number in two column format using MS Sql-Server query.
Just like below attached screen shot image.
So please give any suggestion.
Using a couple of inline tallies would be way faster than a WHILE. This version will go up to 1000 integers (500 rows):
DECLARE #Start int = 1,
#End int = 99;
SELECT TOP(CONVERT(int,CEILING(((#End*1.) - #Start + 1)/2)))
(ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) - 1)*2 + #Start AS Number1,
CASE WHEN (ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) - 1)*2 + #Start +1 <= #End THEN (ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) - 1)*2 + #Start +1 END AS Number2
FROM (VALUES(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL))N1(N)
CROSS APPLY (VALUES(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL))N2(N)
CROSS APPLY (VALUES(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL))N3(N);
An alternative way that looks less messy with the CASE and TOP would be to use a couple of CTEs:
WITH Tally AS(
SELECT TOP(#End - #Start + 1)
ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) - 1 + #Start AS I
FROM (VALUES(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL))N1(N)
CROSS APPLY (VALUES(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL))N2(N)
CROSS APPLY (VALUES(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL))N3(N)),
Numbers AS(
SELECT I AS Number1,
LEAD(I) OVER (ORDER BY I) AS Number2
FROM Tally)
SELECT Number1,
Number2
FROM Numbers
WHERE Number1 % 2 = #Start % 2;
I like to use recursive queries for this:
with cte (num1, num2) as (
select 1, 2
union all
select num1 + 2, num2 + 2 from cte where num2 < 10
)
select * from cte order by num1
You control the maximum number with the inequality condition in the recursive member of the cte.
If you need to generate more than 100 rows, you need to add option(maxrecursion 0) at the very end of the query.
Assuming you are starting with a table with one column, you can use:
select min(number), max(number)
from sample_data
group by floor( (number - 1) / 2);
Alternatively, set-based solution using window functions:
use tempdb
;with sample_data as (
select 1 as val union all
select 2 union all
select 3 union all
select 4 union all
select 5 union all
select 6 union all
select 7 union all
select 8 union all
select 9 union all
select 10
)
, sample_data_split as
(
select
val
, 2- row_number() over (order by val) % 2 as columnid
, NTILE((select count(*) / 2 from sample_data) ) over (order by val) groupid
from sample_data
)
the intermediate result of sample_data_split is:
val columnid groupid
1 1 1
2 2 1
3 1 2
4 2 2
5 1 3
6 2 3
7 1 4
8 2 4
9 1 5
10 2 5
and then to get the resultset into a desired format:
select
min(case when columnid = 1 then val end) as column1
, min(case when columnid = 2 then val end) as column2
from sample_data_split
group by groupid
column1 column2
1 2
3 4
5 6
7 8
9 10
Those CTEs can be merged into a single SELECT:
select
min(case when columnid = 1 then val end) as column1
, min(case when columnid = 2 then val end) as column2
from
(
select
val
, 2- row_number() over (order by val) % 2 as columnid
, NTILE((select count(*) / 2 from sample_data) ) over (order by val) groupid
from sample_data
) d
group by groupid
The positive side of a such approach, that it scales well and has no upper boundary on how much rows to be processed
So I got this solution on it as below...
declare #t table
(
id int identity(1,1),
Number_1 int,
Number_2 int
)
declare #min int=1
declare #max int=10
declare #a int=0;
declare #id int=0
while(#min<=#max)
begin
if(#a=0)
begin
insert into #t
select #min,null
set #a=1
end
else if(#a=1)
begin
select top 1 #id=id from #t order by id desc
update #t set Number_2=#min where id=#id
set #a=0
end
set #min=#min+1
end
select Number_1,Number_2 from #t

ROW_Number with Custom Group

I am trying to have row_number based on custom grouping but I am not able to produce it.
Below is my Query
CREATE TABLE mytbl (wid INT, id INT)
INSERT INTO mytbl Values(1,1),(2,1),(3,0),(4,2),(5,3)
Current Output
wid id
1 1
2 1
3 0
4 2
5 3
Query
SELECT *, RANK() OVER(PARTITION BY wid, CASE WHEN id = 0 THEN 0 ELSE 1 END ORDER BY ID)
FROM mytbl
I would like to rank the rows based on custom condition like if ID is 0 then I have start new group until I have non 0 ID.
Expected Output
wid id RN
1 1 1
2 1 1
3 0 1
4 2 2
5 3 2
Guessing here, as we don't have much clarification, but perhaps this:
SELECT wid,
id,
COUNT(CASE id WHEN 0 THEN 1 END) OVER (ORDER BY wid ROWS BETWEEN UNBOUNDED PRECEDING AND 1 PRECEDING) +1 AS [Rank]
FROM mytbl ;
If I understand you correctly, you may use the next approach. Note, that you need to have an ordering column (I assume this is wid column):
Statement:
;WITH ChangesCTE AS (
SELECT
*,
CASE WHEN LAG(id) OVER (ORDER BY wid) = 0 THEN 1 ELSE 0 END AS ChangeIndex
FROM mytbl
), GroupsCTE AS (
SELECT
*,
SUM(ChangeIndex) OVER (ORDER BY wid) AS GroupIndex
FROM ChangesCTE
)
SELECT
wid,
id,
DENSE_RANK() OVER (ORDER BY GroupIndex) AS Rank
FROM GroupsCTE
Result:
wid id Rank
1 1 1
2 1 1
3 0 1
4 2 2
5 3 2
without much clarification on the logic required, my understanding is you want to increase the Rank by 1 whenever id = 0
select wid, id,
[Rank] = sum(case when id = 0 then 1 else 0 end) over(order by wid)
+ case when id <> 0 then 1 else 0 end
from mytbl
Try this,
CREATE TABLE #mytbl (wid INT, id INT)
INSERT INTO #mytbl Values(1,1),(2,1),(3,0)
,(4,2),(5,3),(6,0),(7,4),(8,5),(9,6)
;with CTE as
(
select *,ROW_NUMBER()over(order by wid)rn
from #mytbl where id=0
)
,CTE1 as
(
select max(rn)+1 ExtraRN from CTE
)
select a.* ,isnull(ca.rn,ca1.ExtraRN) from #mytbl a
outer apply(select top 1 * from CTE b
where a.wid<=b.wid )ca
cross apply(select ExtraRN from CTE1)ca1
drop table #mytbl
Here both OUTER APPLY and CROSS APPLY will not increase cardianility estimate.It will always return only one rows.

Getting the minimum of column on the basis of other field

I have a scenario wherein I have
Id|rank| date
1 | 7 |07/08/2015
1 | 7 |09/08/2015
1 | 8 |16/08/2015
1 | 8 |17/08/2015
1 | 7 |19/08/2015
1 | 7 |15/08/2015
2 | 7 |01/08/2015
2 | 7 |02/08/2015
2 | 8 |16/08/2015
2 | 8 |17/08/2015
2 | 7 |26/08/2015
2 | 7 |28/08/2015
My desired solution is
1 | 7 |07/08/2015
1 | 8 |16/08/2015
1 | 7 |15/08/2015
2 | 7 |01/08/2015
2 | 8 |16/08/2015
2 | 7 |26/08/2015
i.e for each block of id and rank I want the minimum of date.
I have tried using while loop as there are thousands of records it is taking 2 hours to load.Is there any other way to do please suggest.
For each row give unique row number using necessary order. (As I get Id is more important than date and date is more important than rank).
Join resulting table to itself using row numbers shifted by one row (d1.RowNum = d2.RowNum+1).
Select only rows that are joined to "other block" rows (d1.Id <> d2.Id or d1.Rank <> d2.rank).
Depending on shifting direction and selected table either maximal or minimal date will be selected.
Don't forget "edge case" - row that due to shifting can't be joined (that's why not inner join and d1.RowNum = 1 condition used).
;WITH dataWithRowNums as (
select Id, Rank, Date,
RowNum = ROW_NUMBER() OVER (ORDER BY Id,date,rank)
from YourTable
)
select d1.Id, d1.Rank, d1.Date
from dataWithRowNums d1
left join dataWithRowNums d2
on d1.RowNum = d2.RowNum+1 and (d1.Id <> d2.Id or d1.Rank <> d2.rank)
where not d2.Id is null or d1.RowNum = 1
This code returns result bit different from yours:
Id Rank Date
1 7 2015-08-07
1 8 2015-08-16
1 7 2015-08-19 <-- you've got here 2015-08-15
2 7 2015-08-01
2 8 2015-08-16
2 7 2015-08-26
As block (Rank 8 Id 1) have started at 16/08 so row 15/08 for rank 7 is related to first block (rank7 Id1).
If you still need your sorting (so 15/08 rank 7 is related to second block (rank7 id1)) then you should provide your own RowSorting data and then ask here about another solution for another task )
Here is the query using row_number()
;WITH cte_rec
as (SELECT Id,Rank,Date
,ROW_NUMBER()OVER (partition by Id,Rank ORDER BY date) as RNO
FROM YourTable)
SELECT Id,Rank,Date
FROM cte_rec
WHERE RNO =1
This is what I have tried and is running as expected
create table #temp
(
iden int identity(1,1),
ID int,
[rank] int,
[date] date,
dr_id int,
rownum_id int,
grouprecord int
)
Insert into #temp(id,rank,date)
select 1 , 7 ,'07/08/2015'
union all select 1 , 7 ,'09/08/2015'
union all select 1 , 8 ,'08/16/2015'
union all select 1 , 8 ,'08/17/2015'
union all select 1 , 7 ,'08/19/2015'
union all select 1 , 7 ,'08/15/2015'
union all select 2 , 7 ,'08/01/2015'
union all select 2 , 7 ,'08/02/2015'
union all select 2 , 8 ,'08/16/2015'
union all select 2 , 8 ,'08/17/2015'
union all select 2 , 7 ,'08/26/2015'
union all select 2 , 7 ,'08/28/2015'
update t1
set dr_id = t2.rn
from #temp t1 inner join
(select iden, dense_rank() over(order by id) as rn from #temp) t2
on t1.iden = t2.iden
update t1
set rownum_id = t2.rn
from #temp t1 inner join
(select iden, row_number() over(partition by dr_id order by id) as rn from #temp) t2
on t1.iden = t2.iden
select *,row_number() over(order by iden)rn into #temp1 from
(
select t2.*
from #temp t1 inner join #temp t2
on (t1.dr_id = t2.dr_id or t2.dr_id = (t1.dr_id +1) ) and ( t1.rank<>t2.rank or t2.dr_id = (t1.dr_id +1) )
and t2.iden = t1.iden + 1
)a
declare #id int,#miniden int,#maxiden int,#maxid int
set #id = 1
select #maxid = max(iden) from #temp
while exists(select 1 from #temp1 where rn = #id)
begin
Select #miniden = iden from #temp1
where rn = #id
Select #maxiden = iden from #temp1
where rn = #id+1
update #temp
set grouprecord = #id +1
where iden between #miniden and #maxiden
IF(#maxiden IS NULL)
BEGIN
Update #temp
set grouprecord = #id +1
where iden between #miniden and #maxid
END
set #id = #id + 1
SET #miniden =NULL
SET #maxiden = NULL
end
UPDATE #TEMP
SET GROUPRECORD = 1
WHERE GROUPRECORD IS NULL
select min(date) as mindate,grouprecord from #temp
group by grouprecord
Thanks everyone the help :)

How to use aggregate function in update in SQL server 2012

I Tried as shown below:
CREATE TABLE #TEMP
(
ID INT,
EmpID INT,
AMOUNT INT
)
INSERT INTO #TEMP VALUES(1,1,10)
INSERT INTO #TEMP VALUES(2,1,5)
INSERT INTO #TEMP VALUES(3,2,6)
INSERT INTO #TEMP VALUES(4,3,8)
INSERT INTO #TEMP VALUES(5,3,10)
.
.
.
SELECT * FROM #TEMP
ID EmpID AMOUNT
1 1 10
2 1 5
3 2 6
4 3 8
5 4 10
UPDATE #TEMP
SET AMOUNT = SUM(AMOUNT) - 11
Where EmpID = 1
Expected Output:
Table consists of employeeID's along with amount assigned to Employee I need to subtract amount from amount filed depending on employee usage. Amount "10" should be deducted from ID = 1 and amount "1" should be deducted from ID = 2.
Amount: Credits available for that particular employee depending on date.
So i need to reduce credits from table depending on condition first i need to subtract from old credits. In my condition i need to collect 11 rupees from empID = 1 so first i need to collect 10 rupee from ID=1 and 1 rupee from the next credit i.e ID=2. For this reason in my expected output for ID=1 the value is 0 and final output should be like
ID EmpID AMOUNT
1 1 0
2 1 4
3 2 6
4 3 8
5 4 10
Need help to update records. Check error in my update statement.
Declare #Deduct int = -11,
#CurrentDeduct int = 0 /*this represent the deduct per row */
update #TEMP
set #CurrentDeduct = case when abs(#Deduct) >= AMOUNT then Amount else abs(#Deduct) end
, #Deduct = #Deduct + #CurrentDeduct
,AMOUNT = AMOUNT - #CurrentDeduct
where EmpID= 1
I think you want the following: subtract amounts from 11 while remainder is positive. If this is true, here is a solution with recursive cte:
DECLARE #t TABLE ( id INT, amount INT )
INSERT INTO #t VALUES
( 1, 10 ),
( 2, 5 ),
( 3, 3 ),
( 4, 2 );
WITH cte
AS ( SELECT * , 17 - amount AS remainder
FROM #t
WHERE id = 1
UNION ALL
SELECT t.* , c.remainder - t.amount AS remainder
FROM #t t
CROSS JOIN cte c
WHERE t.id = c.id + 1 AND c.remainder > 0
)
UPDATE t
SET amount = CASE WHEN c.remainder > 0 THEN 0
ELSE -remainder
END
FROM #t t
JOIN cte c ON c.id = t.id
SELECT * FROM #t
Output:
id amount
1 0
2 0
3 1
4 2
Here I use 17 as start remainder.
If you use sql server 2012+ then you can do it like:
WITH cte
AS ( SELECT * ,
17 - SUM(amount) OVER ( ORDER BY id ) AS remainder
FROM #t
)
SELECT id ,
CASE WHEN remainder >= 0 THEN 0
WHEN remainder < 0
AND LAG(remainder) OVER ( ORDER BY id ) >= 0
THEN -remainder
ELSE amount
END
FROM cte
First you should get a cumulative sum on amount:
select
id,
amount,
sum(amount) over (order by id) running_sum
from #TEMP;
From here we should put 0 on rows before running_sum exceeds the value 11. Update the row where the running sum exceeds 11 and do nothing to rows after precedent row.
select
id,
amount
running_sum,
min(case when running_sum > 11 then id end) over () as decide
from (
select
id,
amount,
sum(amount) over (order by id) running_sum
from #TEMP
);
From here we can do the update:
merge into #TEMP t
using (
select
id,
amount
running_sum,
min(case when running_sum > 11 then id end) over () as decide
from (
select
id,
amount,
sum(amount) over (order by id) running_sum
from #TEMP
)
)a on a.id=t.id
when matched then update set
t.amount = case when a.id = a.decide then a.running_sum - 11
when a.id < a.decide then 0
else a.amount
end;
See an SQLDFIDDLE

Help me with my select query

I have this table in sql server 2005:
id student active
1 Bob 1
3 Rob 0
5 Steve 1
7 John 1
8 Mark 0
10 Dave 0
16 Nick 1
My select query returns an active student by a given id.
But I also want to return the ids of prev and next student who are active. If no prev, it will be 0 or null. Same for next.
Example: for id=5, my select would return
id student prev_id next_id
5 steve 1 7
Example: for id=7, my select would return
id student prev_id next_id
7 John 5 16
Example: for id=16, my select would return
id student prev_id next_id
16 Nick 7 0
How do I write this select query?
I have query but I just can't get the prev id correctly. It always returns the first active id.
Thanks
EDIT:
Here is the query I have right now.
select id, student,
(select top 1 id from test where id<7 and active=1) as prev,
(select top 1 id from test where id>7 and active=1) as next
from test where id=7--I used 7 just as an example. it will be a parameter
try something like this
SELECT ID,
Student,
( SELECT TOP 1
ID
FROM dbo.table AS pT
WHERE pT.ID < T.ID And Active = 1
ORDER BY ID DESC
) AS PrevID,
( SELECT TOP 1
ID
FROM dbo.table AS pT
WHERE pT.ID > T.ID And Active = 1
ORDER BY ID
) AS NextID
FROM dbo.table AS T
Working sample
DECLARE #T TABLE (id int, student varchar(10), active bit)
insert #t select
1 ,'Bob', 1 union all select
3 ,'Rob', 0 union all select
5 ,'Steve', 1 union all select
7 ,'John', 1 union all select
8 ,'Mark', 0 union all select
10 ,'Dave', 0 union all select
16 ,'Nick', 1
---- your query starts below this line
declare #id int set #id = 5
select id, student,
isnull((select top(1) Prev.id from #T Prev
where Prev.id < T.id and Prev.active=1
order by Prev.id desc),0) Prev,
isnull((select top(1) Next.id from #T Next
where Next.id > T.id and Next.active=1
order by Next.id),0) Next
from #T T
where id = #id
The ISNULLs are to return 0 when there is no match - NULL would have worked fine but your question has 0 when there is no Next.
You may want to take a look at Common Table Expression, a feature for only SQL Server for recursive queries, you can find a link here
But this sound like homework, and probebly not the right forum to ask it in.
Regards
You could use a nested query. I obviously can't test this out, but you should get the idea.
SELECT id, student ,
(SELECT C1.id FROM students S1 WHERE S1.active = 1 AND S1.id < S.id LIMIT 1) AS beforeActive,
(SELECT C2.id FROM categories S2 WHERE S2.active = 1 AND S2.id > S.id LIMIT 1) AS afterActive
FROM students S
Efficiency wise, I've no idea how well this query will perform
This will give you a little more control, especially since you are paginating.
WITH NumberedSet AS (
SELECT s.id,
s.student,
row_number() OVER (ORDER BY s.id) AS rownum
FROM dbo.students AS s
WHERE s.active = 1
)
SELECT cur.id,
cur.student,
isnull(prv.id,0) AS prev_id,
isnull(nxt.id,0) AS next_id
FROM NumberedSet AS cur
LEFT JOIN NumberedSet AS prv ON cur.rownum - 1 = prv.rownum
LEFT JOIN NumberedSet AS nxt ON cur.rownum + 1 = nxt.rownum
;