How to assign correlative numbers to rows only using SQL? - sql

I have the following table in an Oracle database:
InvoiceNumber InvoiceDate InvoiceCorrelative
------------- ----------- ------------------
123 02-03-2009 0
124 02-03-2009 0
125 02-04-2009 0
126 02-04-2009 0
127 02-04-2009 0
128 02-05-2009 0
129 02-06-2009 0
130 02-06-2009 0
... ... ...
And I want to set a value for the InvoiceCorrelative column in every row in order to have a sequence of numbers starting at 1 for each date. In the example above I want the table to look like this:
InvoiceNumber InvoiceDate InvoiceCorrelative
------------- ----------- ------------------
123 02-03-2009 1
124 02-03-2009 2
125 02-04-2009 1
126 02-04-2009 2
127 02-04-2009 3
128 02-05-2009 1
129 02-06-2009 1
130 02-06-2009 2
... ... ...
Is it possible to do it only using SQL statements?. I've been playing with rownum but didn't get anywhere.

Try:
ROW_NUMBER() OVER (PARTITION BY InvoiceDate ORDER BY InvoiceNumber)

Using Standard SQL,
Update TableName T Set
InvoiceCorrelative =
(Select Count(*) From TableName
Where InvoiceDate = T.InvoiceDate
And InvoiceNumber <= T.InvoiceNumber)

Related

Get the non 0 value for group ID in the column

I want to print only the IDs which have flag=1 but not have a group value as 0
(i.e print only flag = 1 in all visit)
Sample:
ID Val Flag
123 12 0
123 15 0
123 25 1
123 48 0
321 78 1
321 56 1
456 23 0
456 54 0
789 78 1
Expected Result:
ID
321
789
You can try the below -
select id
from tablename
group by id
having min(flag)=max(flag) and min(flag)=1
You could do the following:
SELECT ID
FROM tablename
WHERE flag != 0

T SQL CTE Previous Row Calculation

I'm using SQL Server 2016.
I have the below table:
SKU Shop Week ShopPrioirty Replen Open_Stk Open_Stk Calc
111 100 1 1 0 17 NULL
111 200 1 2 2 NULL NULL
111 300 1 3 0 NULL NULL
111 400 1 4 0 NULL NULL
222 100 2 1 5 17 NULL
222 200 2 2 5 NULL NULL
222 300 2 3 5 NULL NULL
222 400 2 4 5 NULL NULL
This is the desired result:
SKU Shop Week ShopPrioirty Replen Open_Stk Open_Stk Calc
111 100 1 1 0 17 17
111 200 1 2 2 NULL 17
111 300 1 3 0 NULL 15
111 400 1 4 0 NULL 15
222 100 2 1 20 17 17
222 200 2 2 15 NULL 12
222 300 2 3 12 NULL 7
222 400 2 4 10 NULL 2
I need to update the 'Open_Stk Calc' based on the previous row:
'Open_Stk Calc' - IIF('Replen'<=IIF('Open_Stk'>=0,'Open_Stk',0),'Replen',0)
I am using a CTE to update a row based on a calculation of the previous rows. This is my SQL:
;WITH CTE AS
(
SELECT
SKU,
[Shop],
[Week],
[Store_Priority],
[Replen],
[Open_Stk],
[Open_Stk Calc],
FIRST_VALUE([Open_Stk]) OVER ( PARTITION BY [SKU] ,[Week] ORDER BY [Store_Priority] ROWS UNBOUNDED PRECEDING)
-
ISNULL(SUM(IIF([Replen] <= IIF([Open_Stk]>=0,[Open_Stk],0),[Replen],0))
OVER (PARTITION BY [SKU] ,[Week] ORDER BY [Store_Priority] ROWS BETWEEN UNBOUNDED PRECEDING AND 1 PRECEDING), 0) AS CurrentStock
FROM [tblTEST])
UPDATE CTE
SET [Open_Stk Calc] = CurrentStock
However, this produces the following result:
SKU Shop Week ShopPrioirty Replen Open_Stk Open_Stk Calc
111 100 1 1 0 17 17
111 200 1 2 2 NULL 17
111 300 1 3 0 NULL 17
111 400 1 4 0 NULL 17
And not the desired result - where have I gone wrong?
As one can see in the MS documentation, the OVER clauses supports specific kind of functions:
Ranking functions
Aggregate functions
Analytic functions
NEXT VALUE FOR function
None of them include IIF, as Luis Cazares noted in their comment.
Your code indicates you do have a clue about what you are doing - maybe you forgot to put your IIF inside a SUM?

proc sql statement to sum on values/rows that match a condition

I have a data table like below:
Table 1:
ROWID PERSONID YEAR pidDifference TIMETOEVENT DAYSBETVISIT
10 111 2009 . 100 .
110 120 2009 9 10 .
231 120 2009 0 20 10
222 120 2010 0 40 20
221 222 2009 102 10 30
321 222 2009 0 30 20
213 222 2009 0 10 20
432 321 2009 99 10 0
211 432 2009 111 20 10
212 432 2009 0 20 0
I want to sum over the DAYSBETVISIT column only when the pidDifference value is 0 for each PERSONID. So I wrote the following proc sql statement.
proc sql;
create table table5 as
(
select rowid, YEAR, PERSONID, pidDifference, TIMETOEVENT, DAYSBETVISIT,
SUM(CASE WHEN PIDDifference = 0 THEN DaysBetVisit ELSE 0 END)
from WORK.Table4_1
group by PERSONID,TIMETOEVENT, YEAR
);
quit;
However, the result I got was not summing the DAYSBETVISIT values in rows where PIDDifference = 0 within the same PERSONID. It just output the same value as was present in DAYSBETVISIT in that specific row.
Column that I NEED (sumdays) but don't get with above statement (showing the resultant column using above statement as OUT:
ROWID PERSONID YEAR pidDifference TIMETOEVENT DAYSBETVISIT sumdays OUT
10 111 2009 . 100 . 0 0
110 120 2009 9 10 . 0 0
231 120 2009 0 20 10 30 10
222 120 2010 0 40 20 30 20
221 222 2009 102 10 30 0 0
321 222 2009 0 30 20 40 20
213 222 2009 0 10 20 40 20
432 321 2009 99 10 0 0 0
211 432 2009 111 20 10 0 0
212 432 2009 0 20 0 0 0
I do not know what I am doing wrong.
I am using SAS EG Version 7.15, Base SAS version 9.4.
For your example data it looks like you just need to use two CASE statements. One to define which values to SUM() and another to define whether to report the SUM or not.
proc sql ;
select personid, piddifference, daysbetvisit, sumdays
, case when piddifference = 0
then sum(case when piddifference=0 then daysbetvisit else 0 end)
else 0 end as WANT
from expect
group by personid
;
quit;
Results
pid
PERSONID Difference DAYSBETVISIT sumdays WANT
--------------------------------------------------------
111 . . 0 0
120 0 10 30 30
120 0 20 30 30
120 9 . 0 0
222 0 20 40 40
222 0 20 40 40
222 102 30 0 0
321 99 0 0 0
432 0 0 0 0
432 111 10 0 0
SAS proc sql doesn't support window functions. I find the re-merging aggregations to be a bit difficult to use, except in the obvious cases. So, use a subquery or join and group by:
proc sql;
create table table5 as
select t.rowid, t.YEAR, t.PERSONID, t.pidDifference, t.TIMETOEVENT, t.DAYSBETVISIT,
tt.sum_DaysBetVisit
from WORK.Table4_1 t left join
(select personid, sum(DaysBetVisit) as sum_DaysBetVisit
from WORK.Table4_1
group by personid
having min(pidDifference) = max(pidDifference) and min(pidDifference) = 0
) tt
on tt.personid = t.personid;
Note: This doesn't handle NULL values for pidDifference. If that is a concern, you can add count(pidDifference) = count(*) to the having clause.

group by column not having specific value

I am trying to obtain a list of Case_Id's where the case does not contain a specific RoleId using Microsoft Sql Server 2012.
For example, I would like to obtain a collection of Case_Id's that do not contain a RoleId of 4.
So from the data set below the query would exclude Case_Id's 49, 50, and 53.
Id RoleId Person_Id Case_Id
--------------------------------------
108 4 108 49
109 1 109 49
110 4 110 50
111 1 111 50
112 1 112 51
113 2 113 52
114 1 114 52
115 7 115 53
116 4 116 53
117 3 117 53
So far I have tried the following
SELECT Case_Id
FROM [dbo].[caseRole] cr
WHERE cr.RoleId!=4
GROUP BY Case_Id ORDER BY Case_Id
The not exists operator seems to fit your need exactly:
SELECT DISTINCT Case_Id
FROM [dbo].[caseRole] cr
WHERE NOT EXISTS (SELECT *
FROM [dbo].[caseRole] cr_inner
WHERE cr_inner.Case_Id = cr.case_id
AND cr_inner.RoleId = 4);
Just add a having clause instead of where:
SELECT Case_Id
FROM [dbo].[caseRole] cr
GROUP BY Case_Id
HAVING SUM(case when cr.RoleId = 4 then 1 else 0 end) = 0
ORDER BY Case_Id;

Using sequences to create group ID

I'm attempting to create group_ids based on a set of item_ids. The only indication that the item_ids are part of a single group is the fact that item_ids are sequential. For example, based on the first two columns below, the output I want is the third:
item item_id group_id
ABC 282 2
ABC 283 2
ABC 284 2
ABC 285 2
ABC 051 3
ABC 052 3
ABC 189 4
ABC 231 5
ABC 232 5
ABC 233 5
ABC 234 5
ABC 247 6
ABC 248 6
ABC 249 6
ABC 250 6
ABC 091 7
ABC 092 7
The group_id doesn't necessarily have to be sequential itself, it only has to be unique. I attempted this with the following code:
create sequence seq
start with 1
minvalue 1
increment by 1
cache 20;
select seq.nextval from dual; --to initialize the sequence
select
item,
item_id,
case when diff = 1 then seq.currval else seq.nextval end group_id
from
(
select
item,
item_id,
(id - lag(id, 1, 0) over (order by 1) diff
from
(
select
item,
item_id
from
table
)
);
But get the following output:
item item_id group_id
ABC 282 2
ABC 283 3
ABC 284 4
ABC 285 5
ABC 051 6
ABC 052 7
ABC 189 8
ABC 231 9
ABC 232 10
ABC 233 11
ABC 234 12
ABC 247 13
ABC 248 14
ABC 249 15
ABC 250 16
ABC 091 17
ABC 092 18
When looking for the cause of the problem, I found an excellent explanation by user ShannonSeverance that details why my solution won't work. However, it didn't provide any suggestions on how to move forward.
Does anyone have any ideas?
You have a problem, because SQL tables are inherently unordered. The following "should" logically work, although it won't in practice:
select ii.*, (item_id - rownum) as grp_id
from item_ids ii;
A sequence of item_ids in order minus the row number is constant. You can use that for a group, at least for a given item. To handle multiple items, concatenate the values together:
select ii.*, item||'-'||(item_id - rownum) as grp_id
from item_ids ii;
To really make this work, you need to add an order by -- this guarantees the ordering of the results from the select. This might work, assuming that there are "holes" between the groups:
select ii.*, item||'-'||(item_id - rownum) as grp_id
from item_ids ii
order by item, item_id;
Otherwise, you need some other column to determine the proper ordering for the items.