SQL to pick the next value - sql

I have a table of values. Each value may have 1 or more entry, but only 1 should be active at any one time. The table has a primary INT ID
I need a method to make the 'current' value inactive and make the 'next' value the active value. If the current active value is the last active, instead make the first value active. Values with only 1 entry will always be active.
The sequence should work like below
Is anyone able to provide a way to achieve this?

You should not be showing runs in separate columns. Your data should put this information in separate rows. So your data should have a separate set of rows for each run:
id value run active
1 Apple 1 1
2 Apple 1 0
3 Apple 1 0
4 Banana 1 1
5 Banana 1 0
6 Cherry 1 1
1 Apple 2 0
2 Apple 2 1
3 Apple 2 0
4 Banana 2 0
5 Banana 2 1
6 Cherry 2 1
You can add the next run as:
with r as
select t.*, max(run) over () as max_run,
row_number() over (partition by run, value order by id) as seqnum,
lag(active) over (partition by run, value order by id) as prev_active
from runs
)
insert into runs (id, value, run, active)
select id, value, max_run + 1,
(case when prev_active = 1 then 1
when prev_active is null or seqnum = 1 then 1
else 0
end) as active
from r
where run = max_run;

Simply make a check, that is select id from the table is not max(id) of that table, then update the log to inactive and then update the id+1 to active.
And if select id from the table is max(ID) then simply update that row to inactive and update min(ID) to active.
build the query, itll be fun.

Related

Teradata/SQL, select all rows until a certain value is reached per partition

I'd like to select all rows from a table until (and including) a certain value is reached per partition. In this case all rows per id that precede when status has the value 'b' for the last time. Note: the timestamp is in order per id
id
name
status
status
timestamp
1
Sta
open
a
10:50:09.000000
1
Danny
open
c
10:50:19.000000
1
Elle
closed
b
10:50:39.000000
2
anton
closed
a
16:00:09.000000
2
jill
done
b
16:00:19.000000
2
tom
open
b
16:05:09.000000
2
bill
open
c
16:07:09.000000
3
ann
done
b
08:00:13.000000
3
stef
done
b
08:12:13.000000
3
martin
open
b
08:25:13.000000
3
jeff
open
a
09:00:13.000000
3
luke
open
c
09:07:13.000000
3
karen
open
c
09:15:13.000000
3
lucy
open
a
10:00:13.000000
The output would look like this:
id
name
status
status
timestamp
1
Sta
open
a
10:50:09.000000
1
Danny
open
c
10:50:19.000000
1
Elle
closed
b
10:50:39.000000
2
anton
closed
a
16:00:09.000000
2
jill
done
b
16:00:19.000000
2
tom
open
b
16:05:09.000000
3
ann
done
b
08:00:13.000000
3
stef
done
b
08:12:13.000000
3
martin
open
b
08:25:13.000000
I've tried to solve this using qualify with rank etc. but unfortunately with no succes. would be appreciated if somebody would be able to help me!
all rows per id that precede when status has the value 'b' for the last time is the same as no rows before value 'b' occurs the first time when you revert the sort order:
SELECT *
FROM tab
QUALIFY -- tag the last 'b'
Count(CASE WHEN status = 'b' THEN 1 end)
Over (PARTITION BY id
ORDER BY timestamp DESC
ROWS Unbounded Preceding) > 0
ORDER BY id, timestamp
;
This will not return ids where no 'b' exists.
If you want to return those, too, add another condition to QUALIFY:
OR -- no 'b' found
Count(CASE WHEN status = 'b' THEN 1 end)
Over (PARTITION BY id) = 0
As both counts share the same partition, it's still a single STAT step in Explain.

Creating duplicating rank over multiple columns

I have data as below where for one customer ID there are several orders (KEY) which is the primary key. I have also have a activity flag as below (either 0 or 1).
CUST_ID KEY FLAG
1 1 1
1 2 1
1 3 1
1 4 0
1 5 0
1 6 1
1 7 1
1 8 0
1 9 0
Now I want to create ranks as below based on the FLAG. The idea is to give same Rank as preceding row if the FLAG is same as preceding row. The Rank increments if the current value is different from preceding value.
CUST_ID KEY FLAG RN
1 1 1 1
1 2 1 1
1 3 1 1
1 4 0 2
1 5 0 2
1 6 1 3
1 7 1 3
1 8 0 4
1 9 0 4
I'm new to SQL, so please let me know if I need to reframe my question.
Use LAG() window function to get each row's previous flag and then use SUM() window function to create the rankings:
SELECT CUST_ID, KEY, FLAG,
SUM(CASE WHEN FLAG <> prev_FLAG THEN 1 END) OVER (PARTITION BY CUST_ID ORDER BY KEY) RN
FROM (
SELECT *, LAG(FLAG, 1, FLAG - 1) OVER (PARTITION BY CUST_ID ORDER BY KEY) prev_FLAG
FROM tablename
) t;
See the demo.
The code could be simplified, depending on the specific database that you use.

Update rows based on rownumber in SQL Server 2012

Ive been given some data in a spreadsheet which will soon be going into an automated import so i cannot do any manual entry on the spreadsheet. The data basically has the following columns. Trayid, trayname, itemdescription and rownumber. I didnt build these tables myself or i would of built it differently but i have to stick to the format which is already set.
The Data that is being imported will look at followed.
Trayid | Trayname | ItemDescription | RowNumber
1 Tray 1 Product 1 1
Product 2 2
Product 3 3
Product 4 4
2 Tray 2 Product 1 1
Product 2 2
Product 3 3
Product 4 4
Product 5 5
What i need to do is update the trayid and trayname for each of the other rows following row 1, so for example it will look like.
Trayid | Trayname | ItemDescription | RowNumber
1 Tray 1 Product 1 1
1 Tray 1 Product 2 2
1 Tray 1 Product 3 3
1 Tray 1 Product 4 4
2 Tray 2 Product 1 1
2 Tray 2 Product 2 2
2 Tray 2 Product 3 3
2 Tray 2 Product 4 4
2 Tray 2 Product 5 5
Im guessing i need to use a curser or something but im not sure, i think it can be done by going down the rownumbers and stopping when it see's rownumber 1 again and then carrying on with the next trayid and trayname.
Sorry if what i need doesnt make sense, it was awkward to explain.
SQL tables have no inherent ordering. So you cannot depend on that. But, there is something that you can do:
Define an identity column in the source table.
Create a view on the source table that excludes the identity.
Bulk insert into the view.
This will assign a sequential number to rows in the same order as the original data. Let's call this id. Then you can do your update by doing:
with toupdate (
select t.*,
max(TrayId) over (partition by grp) as new_TrayId,
max(TrayName) over (partition by grp) as new_TrayName
from (select t.*,
count(TrayId) over (order by id) as grp
from t
) t
)
update toupdate
set TrayId = new_TrayId,
TrayName = new_TrayName
where TrayId is null;
The idea is to define groups of rows corresponding to each tray. The simple idea is to count the number of non-NULL values before any given row -- everything in a group will then have the same grp value. Window functions then spread the actual value through all rows in the group (using max()), and these values are used for the update.

PSQL get duplicate row

I have table like this-
id object_id product_id
1 1 1
2 1 1
4 2 2
6 3 2
7 3 2
8 1 2
9 1 1
I want to delete all rows except these-
1 1 1
4 2 2
6 3 2
9 1 2
Basically there are duplicates and I want to remove them but keep one copy intact.
what would be the most efficient way for this?
If this is a one-off then you can simply identify the records you want to keep like so:
SELECT MIN(id) AS id
FROM yourtable
GROUP BY object_id, product_id;
You want to check that this works before you do the next thing and actually throw records out. To actually delete those duplicate records you do:
DELETE FROM yourtable WHERE id NOT IN (
SELECT MIN(id) AS id
FROM yourtable
GROUP BY object_id, product_id
);
The MIN(id) obviously always returns the record with the lowest id for a set of (object_id, product_id). Change as desired.

Inserting a new indicator column to tell if a given row maximizes another column in SQL

I currently have a table in SQL that looks like this
PRODUCT_ID_1 PRODUCT_ID_2 SCORE
1 2 10
1 3 100
1 10 3000
2 10 10
3 35 100
3 2 1001
That is, PRODUCT_ID_1,PRODUCT_ID_2 is a primary key for this table.
What I would like to do is use this table to add in a row to tell whether or not the current row is the one that maximizes SCORE for a value of PRODUCT_ID_1.
In other words, what I would like to get is the following table:
PRODUCT_ID_1 PRODUCT_ID_2 SCORE IS_MAX_SCORE_FOR_ID_1
1 2 10 0
1 3 100 0
1 10 3000 1
2 10 10 1
3 35 100 0
3 2 1001 1
I am wondering how I can compute the IS_MAX_SCORE_FOR_ID_1 column and insert it into the table without having to create a new table.
You can try like this...
Select PRODUCT_ID_1, PRODUCT_ID_2 ,SCORE,
(Case when b.Score=
(Select Max(a.Score) from TableName a where a.PRODUCT_ID_1=b. PRODUCT_ID_1)
then 1 else 0 End) as IS_MAX_SCORE_FOR_ID_1
from TableName b
You can use a window function for this:
select product_id_1,
product_id_2,
score,
case
when score = max(score) over (partition by product_id_1) then 1
else 0
end as is_max_score_for_id_1
from the_table
order by product_id_1;
(The above is ANSI SQL and should run on any modern DBMS)