I have an SQL table in SQL Server 2008 which for this purpose includes
Patient_id (Primary), Custno (Char), Recip_Id (Int)
1 - C01731 - 1
2 - C01731 - 2
3 - C01731 - 3
4 - C01732 - 1
5 - C01732 - 2
6 - C01732 - 3
7 - C01732 - 4
8 - C01733 - 1
9 - C01733 - 2
So the stored procedure I am using to move records which would basically reassign the Custno is as follows...
ALTER PROCEDURE [dbo].[JR_SP_BatchMovePatients]
#IDs tblDeletePatients Readonly,
#CustnoTo varchar(max)
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
DECLARE #rownum int
IF EXISTS(SELECT * FROM UsersMailingData WHERE custno = #CustnoTo)
SET #rownum = (select MAX(recip_id)+1 FROM UsersMailingData WHERE custno = #CustnoTo);
ELSE
SET #rownum = 0;
-- Insert statements for procedure here
With Summary AS (
Select UsersMailingData.Patient_id,
UsersMailingData.custno,
UsersMailingData.Recip_id,
row_number() over (order by Custno)+#rownum AS NewNum
FROM UsersMailingData
WHERE Patient_ID IN (Select Patient_id FROM #IDs))
UPDATE Summary
SET Custno = #CustnoTo, Recip_id = NewNum
END
However because I can not use row_number() in this manner I am having a hard time completing this task. So I am passing a table of Patient_id's which match the Patient_id's I want to update and I need to update the Custno field to the #CustnoTo value while reassigning the Recip_id based on the highest Recip_id in the #CustnoTo value.
So if I was moving C01732 to C01731 the results would be as follows...
1 - C01731 - 1
2 - C01731 - 2
3 - C01731 - 3
4 - C01731 - 4
5 - C01731 - 5
6 - C01731 - 6
7 - C01731 - 7
8 - C01733 - 1
9 - C01733 - 2
Any help would be appreciated.
You can try combination of below queries:
update UsersMailingData
set Custno=#CustnoTo
where Custno=#oldCustno
update u
set recip_id=r
(select recip_id,row_number() over (partition by Custno order by Patient_id) as r from UsersMailingData )u
And your Sp should be like
CREATE PROCEDURE [dbo].[JR_SP_BatchMovePatients]
#oldCustno varchar(max),
#CustnoTo varchar(max)
AS
BEGIN
SET NOCOUNT ON;
update UsersMailingData
set Custno=#CustnoTo
where Custno=#oldCustno
update u
set recip_id=r
(select recip_id,row_number() over (partition by Custno order by Patient_id) as r from UsersMailingData )u
END
Related
I'm using SQL Server 2016.
I have this table:
RowID SKU Shop Week Prioirty Replen Open_Stk
---------------------------------------------------------------
1 111 100 1 1 400 5000
2 111 200 1 2 400 NULL
3 111 300 1 3 400 NULL
4 111 400 1 4 400 NULL
This is the desired result:
RowID SKU Shop Week Prioirty Replen Open_Stk
---------------------------------------------------------------
1 111 100 1 1 400 5000
2 111 200 1 2 400 4600
3 111 300 1 3 400 4200
4 111 400 1 4 400 3800
The calculation for Open_Stk is based on the previous row:
[Open_Stk] = [Open_Stk]-IIF([Replen]<=IIF([Open_Stk]>=0,[Open_Stk],0),[Replen],0)
I am using the below cursor to update the Open_Stk but nothing happens - what am I missing:
DECLARE #CurrentRow INT;
DECLARE #PreviousRow INT
DECLARE ShopRank CURSOR FOR
SELECT RowID
FROM [tmp_tblTEST]
ORDER BY [SKU], [Week],Priority
OPEN ShopRank
FETCH NEXT FROM ShopRank INTO #CurrentRow
WHILE ##FETCH_STATUS = 0
BEGIN
IF ((SELECT [Open_Stk] FROM [tmp_tblTEST] WHERE RowID = #CurrentRow) IS NULL)
BEGIN
UPDATE [tmp_tblTEST]
SET [Open_Stk] = [Open_Stk] - IIF([Replen] <= IIF([Open_Stk] >= 0, [Open_Stk], 0), [Replen], 0)
WHERE RowID = #PreviousRow
END
SET #PreviousRow = #CurrentRow
FETCH NEXT FROM ShopRank INTO #CurrentRow
END
CLOSE ShopRank
DEALLOCATE ShopRank
There's no need for a CURSOR here at all. This is a little bit of guess work, but I suspect what you are actually after here is something like this:
SELECT V.RowID,
V.SKU,
V.Shop,
V.[Week],
V.Priority,
V.Replen,
FIRST_VALUE(V.Open_Stk) OVER (PARTITION BY V.SKU ORDER BY V.[Week], V.Priority
ROWS UNBOUNDED PRECEDING) -
ISNULL(SUM(V.Replen) OVER (PARTITION BY V.SKU ORDER BY V.[Week], V.Priority
ROWS BETWEEN UNBOUNDED PRECEDING AND 1 PRECEDING),0) AS OpenStk
FROM (VALUES (1,111,100,1,1,400,5000),
(2,111,200,1,2,400,NULL),
(3,111,300,1,3,400,NULL),
(4,111,400,1,4,400,NULL))V(RowID,SKU,Shop,[Week],Priority,Replen,Open_Stk)
ORDER BY V.Sku,
V.[Week],
V.Priority;
DB<>Fiddle (using original solution)
FIRST_VALUE does what is says on the tin. The SUM subtracts the values from every prior row from the value of Open_Stk on the first row; making the final result set. It only references the prior rows due to the ROWS BETWEEN clause. ROWS UNBOUNDED means to start at the beginning of the partitioned range, and 1 PRECEDING means the row prior.
WITH result AS
(
SELECT
a.*, ISNULL(NULLIF(a.Open_Stk, 0), 0) AS Output
FROM
table1 a
JOIN
table1 b ON a.Prioirty = b.Prioirty - 1
UNION ALL
SELECT
a.*, output - a.Replen
FROM
table1 a
JOIN
result b ON a.Prioirty = b.Prioirty+1
)
SELECT *
FROM result
WHERE output > 0
I'm using MS SQL Server and I have the below table 'tmp_AVG_Weekly_Sales':
RowID SKU Shop Week Avg_Value LAMBDA PMF Value
1 ABC 200 2 1 2 0.13 NULL
2 DEF 250 2 2 4 0.018 NULL
3 XYZ 300 3 3 6 0.0024 NULL
I need to work out the Value field based on the below logic - I am using a Cursor and Loop:
DECLARE #CUMULATIVE AS FLOAT = 0;
DECLARE #COUNT AS INT = 0;
DECLARE #LAMBDA AS FLOAT;
DECLARE #RowID AS INT;
DECLARE #PoissonCursor AS CURSOR;
DECLARE #THRESHOLD AS FLOAT = 0.99;
DECLARE #PMF AS FLOAT --= EXP(-#LAMBDA)
SET #PoissonCursor = CURSOR FOR
SELECT RowID
FROM
[tmp_AVG_Weekly_Sales]
OPEN #PoissonCursor;
FETCH NEXT FROM #PoissonCursor INTO #RowID;
WHILE ##FETCH_STATUS = 0
BEGIN
SELECT #LAMBDA = LAMBDA FROM [tmp_AVG_Weekly_Sales] WHERE RowID = #RowID
SELECT #PMF = PMF FROM [tmp_AVG_Weekly_Sales] WHERE RowID = #RowID
WHILE (#CUMULATIVE < #Threshold)
BEGIN
SET #CUMULATIVE += #PMF
SET #COUNT += 1
SET #PMF = #PMF * (#LAMBDA / #COUNT)
END
UPDATE [tmp_AVG_Weekly_Sales] SET [Value] = #COUNT - 1 WHERE RowID = #RowID
FETCH NEXT FROM #PoissonCursor INTO #RowID;
END
However, the above is just populating the Value field with the same value:
RowID SKU Shop Week Avg_Value LAMBDA PMF Value
1 ABC 200 2 1 2 0.13 6
2 DEF 250 2 2 4 0.018 6
3 XYZ 300 3 3 6 0.0024 6
When I am expecting the below:
RowID SKU Shop Week Avg_Value LAMBDA PMF Value
1 ABC 200 2 1 2 0.13 6
2 DEF 250 2 2 4 0.018 9
3 XYZ 300 3 3 6 0.0024 12
Where am I going wrong?
You never reset #CUMULATIVE or increase #Threshold, so the block of SET calls are only executed the first go through and each subsequent UPDATE just uses those original values.
Having data like this:
id text bit date
1 row 1 2016-11-24
2 row 1 2016-11-25
3 row 0 2016-11-26
4 row 1 2016-11-27
I want to select the data based on where the text and bit columns are distinct, but based on some order, in this case the id, the data changes between two identical rows, it should duplicate this row on the selection.
So, if I use distinct on SQL, I would get rows 1 and 3, but I want to retreive rows 1, 3 and 4, because even 1 and 4 being identical, row 3 is between then when ordering by id.
With a larger dataset, like:
id text bit date
1 row 1 2016-11-24
2 row 1 2016-11-25
3 row 0 2016-11-26
4 row 1 2016-11-27
5 foo 1 2016-11-28
6 bar 1 2016-11-29
7 row 1 2016-11-30
8 row 0 2016-12-01
9 row 0 2016-12-02
10 row 1 2016-12-03
Again, selecting with distinct on text and bit columns, the query would retrieve rows 1,3,5 and 6, but actually I want rows 1,3,4,5,6,7,8 and 10.
;with tb(id,[text],[bit],[date]) AS (
SELECT 1,'row',1,'2016-11-24' union
SELECT 2,'row',1,'2016-11-25' union
SELECT 3,'row',0,'2016-11-26' union
SELECT 4,'row',1,'2016-11-27' union
SELECT 5,'foo',1,'2016-11-28' union
SELECT 6,'bar',1,'2016-11-29' union
SELECT 7,'row',1,'2016-11-30' union
SELECT 8,'row',0,'2016-12-01' union
SELECT 9,'row',0,'2016-12-02' union
SELECT 10,'row',1,'2016-12-03')
select t1.* from tb as t1
OUTER APPLY (select top 1 [text],[bit] from tb as tt where tt.id<t1.id order by id desc ) as t2
where t1.[text]!=isnull(t2.[text],'') or t1.[bit]!=isnull(t2.[bit],1-t1.[bit])
result set:
1 row 1 2016-11-24
3 row 0 2016-11-26
4 row 1 2016-11-27
5 foo 1 2016-11-28
6 bar 1 2016-11-29
7 row 1 2016-11-30
8 row 0 2016-12-01
10 row 1 2016-12-03
It seems that you need a row-by-row operator. You need to know if the new row is the same as the previous one or not. If it is, neglect it, if not, keep it. Here is my solution:
declare #text varchar(100)=(select [text] from Mytable where id = 1)
declare #bit bit = (select [bit] from Mytable where id = 1)
declare #Newtext varchar(100)
declare #Newbit bit
declare #Mytable table(id int, [text] varchar(100), [bit] bit)
Insert into #Mytable select id,text, bit from Mytable where id = 1
declare #counter int =2
while #counter<=(select COUNT(*) from MyTable)
Begin
select #Newtext=(select [text] from Mytable where id = #counter)
select #Newbit=(select [bit] from Mytable where id = #counter)
IF #Newtext!=#text or #Newbit!=#bit
Begin
Insert into #Mytable
select * from Mytable where id = #counter
End
set #text = #Newtext
set #bit = #Newbit;
set #counter = #counter+1
END
select * from #Mytable
I'm trying to generate the numbers in the "x" column considering the values in field "eq", in a way that it should assign a number for every record until it meets the value "1", and the next row should reset and start counting again. I've tried with row_number, but the problem is that I only have ones and zeros in the column I need to evaluate, and the cases I've seen using row_number were using growing values in a column. Also tried with rank, but I haven't managed to make it work.
nInd Fecha Tipo #Inicio #contador_I #Final #contador_F eq x
1 18/03/2002 I 18/03/2002 1 null null 0 1
2 20/07/2002 F 18/03/2002 1 20/07/2002 1 1 2
3 19/08/2002 I 19/08/2002 2 20/07/2002 1 0 1
4 21/12/2002 F 19/08/2002 2 21/12/2002 2 1 2
5 17/03/2003 I 17/03/2003 3 21/12/2002 2 0 1
6 01/04/2003 I 17/03/2003 4 21/12/2002 2 0 2
7 07/04/2003 I 17/03/2003 5 21/12/2002 2 0 3
8 02/06/2003 F 17/03/2003 5 02/06/2003 3 0 4
9 31/07/2003 F 17/03/2003 5 31/07/2003 4 0 5
10 31/08/2003 F 17/03/2003 5 31/08/2003 5 1 6
11 01/09/2005 I 01/09/2005 6 31/08/2003 5 0 1
12 05/09/2005 I 01/09/2005 7 31/08/2003 5 0 2
13 31/12/2005 F 01/09/2005 7 31/12/2005 6 0 3
14 14/01/2006 F 01/09/2005 7 14/01/2006 7 1 4
There is another solution available:
select
nind, eq, row_number() over (partition by s order by s)
from (
select
nind, eq, coalesce((
select sum(eq) +1 from mytable pre where pre.nInd < mytable.nInd)
,1) s --this is the sum of eq!
from mytable) g
The inner subquery creates groups sequentially for each occurrence of 1 in eq. Then we can use row_number() over partition to get our counter.
Here is an example using Sql Server
I have two answers here. One is based off of the ROW_NUMBER() and the other is based off of what appears to be your index (nInd). I wasn't sure if there would be a gap in your index so I made the ROW_NUMBER() as well.
My table format was as follows -
myIndex int identity(1,1) NOT NULL
number int NOT NULL
First one is ROW_NUMBER()...
WITH rn AS (SELECT *, ROW_NUMBER() OVER (ORDER BY myIndex) AS rn, COUNT(*) AS max
FROM counting c GROUP BY c.myIndex, c.number)
,cte (myIndex, number, level, row) AS (
SELECT r.myIndex, r.number, 1, r.rn + 1 FROM rn r WHERE r.rn = 1
UNION ALL
SELECT r1.myIndex, r1.number,
CASE WHEN r1.number = 0 AND r2.number = 1 THEN 1
ELSE c.level + 1
END,
row + 1
FROM cte c
JOIN rn r1
ON c.row = r1.rn
JOIN rn r2
ON c.row - 1 = r2.rn
)
SELECT c.myIndex, c.number, c.level FROM cte c OPTION (MAXRECURSION 0);
Now the index...
WITH cte (myIndex, number, level) AS (
SELECT c.myIndex + 1, c.number, 1 FROM counting c WHERE c.myIndex = 1
UNION ALL
SELECT c1.myIndex + 1, c1.number,
CASE WHEN c1.number = 0 AND c2.number = 1 THEN 1
ELSE c.level + 1
END
FROM cte c
JOIN counting c1
ON c.myIndex = c1.myIndex
JOIN counting c2
ON c.myIndex - 1 = c2.myIndex
)
SELECT c.myIndex - 1 AS myIndex, c.number, c.level FROM cte c OPTION (MAXRECURSION 0);
The answer that I have now is via using
Cursor
I know if there is another solution without cursor it will be better for performance aspects
here is a quick demo of my solution:
-- Create DBTest
use master
Go
Create Database DBTest
Go
use DBTest
GO
-- Create table
Create table Tabletest
(nInd int , eq int)
Go
-- insert dummy data
insert into Tabletest (nInd,eq)
values (1,0),
(2,1),
(3,0),
(4,1),
(5,0),
(6,0),
(7,0),
(8,0),
(9,1),
(8,0),
(9,1)
Create table #Tabletest (nInd int ,eq int ,x int )
go
DECLARE #nInd int , #eq int , #x int
set #x = 1
DECLARE db_cursor CURSOR FOR
SELECT nInd , eq
FROM Tabletest
order by nInd
OPEN db_cursor
FETCH NEXT FROM db_cursor INTO #nInd , #eq
WHILE ##FETCH_STATUS = 0
BEGIN
if (#eq = 0)
begin
insert into #Tabletest (nInd ,eq ,x) values (#nInd , #eq , #x)
set #x = #x +1
end
else if (#eq = 1)
begin
insert into #Tabletest (nInd ,eq ,x) values (#nInd , #eq , #x)
set #x = 1
end
FETCH NEXT FROM db_cursor INTO #nInd , #eq
END
CLOSE db_cursor
DEALLOCATE db_cursor
select * from #Tabletest
The end result set will be as following:
Hope it helps.
Looking at this a slightly different way (which might not be true, but eliminates the need for cursors of recursive CTEs), it looks like you building ordered groups within your dataset. So, start by finding those groups, then determining the ordering of each of them.
The real key is to determine the rules to find the correcting grouping. Based on your description and comments, I'm guessing the grouping is from the start (ordered by the nInd column) ending at each row with and eq value of 1, so you can do something like:
;with ends(nInd, ord) as (
--Find the ending row for each set
SELECT nInd, row_number() over(order by nInd)
FROM mytable
WHERE eq=1
), ranges(sInd, eInd) as (
--Find the previous ending row for each ending row, forming a range for the group
SELECT coalesce(s.nInd,0), e.nInd
FROM ends s
right join ends e on s.ord=e.ord-1
)
Then, using these group ranges, you can find the final ordering of each:
select t.nInd, t.Fecha, t.eq
,[x] = row_number() over(partition by sInd order by nInd)
from ranges r
join mytable t on r.sInd < t.nInd
and t.nInd <= r.eInd
order by t.nInd
I have a table like
Id Value
1 Start
2 Normal
3 End
4 Normal
5 Start
6 Normal
7 Normal
8 End
9 Normal
I have to bring the output like
id Value
1 Start
2 Normal
3 End
5 Start
6 Normal
7 Normal
8 End
i.e. the records between Start & End. Records with id's 4 & 9 are outside the Start & End henceforth are not there in the output.
How to do this in set based manner (SQLServer 2005)?
Load a table #t:
declare #t table(Id int,Value nvarchar(100));
insert into #t values (1,'Start'),(2,'Normal'),(3,'End'),(4,'Normal'),(5,'Start'),(6,'Normal'),(7,'Normal'),(8,'End'),(9,'Normal');
Query:
With RangesT as (
select Id, (select top 1 Id from #t where Id>p.Id and Value='End' order by Id asc) Id_to
from #t p
where Value='Start'
)
select crossT.*
from RangesT p
cross apply (
select * from #t where Id>=p.Id and Id<=Id_to
) crossT
order by Id
Note that I'm assuming no overlaps. The result:
Id Value
----------- ------
1 Start
2 Normal
3 End
5 Start
6 Normal
7 Normal
8 End