SQL Update where select top(num) - sql

I want to UPDATE a column in the same table based on if 4 records are returned by the subquery.
This is the table structure
Id Form Quantity Transitdays percentUnderQty percentOverQty
192 0 15500 5 13950 17050
193 0 1250 5 1125 1375
194 0 5000 5 4500 5500
195 0 12500 4 11250 13750
196 0 164250 3 147825 180675
197 0 5250 3 4725 5775
198 0 6250 3 5625 6875
199 0 12250 3 11025 13475
200 0 1750 2 1575 1925
201 0 17000 2 15300 18700
202 0 2500 2 2250 2750
203 0 11500 2 10350 12650
204 0 1250 1 1125 1375
This is my attempt so far. This statement is updating all of the records and not just specific ones based on the subquery.
UPDATE Temp_Runlist
SET Form = (
SELECT MAX(Quantity) + 1 AS Form
FROM Temp_Runlist AS t2
)
WHERE EXISTS (
SELECT TOP (4) Quantity
FROM Temp_Runlist AS t1
WHERE (Quantity BETWEEN 1125 AND 1375) AND (Form = 0)
)
Logic Explanation:
I will loop over each quantity and call this query. The query should update the Form column with values starting at 1. In the query example below the result should be that the query would not update form because the subquery only found 2 matching records. If the subquery returned 4 records then the Form column of those 4 records would be 1. On the next pass then form would be 2 if only the subquery returns four matches.

Maybe there's a better way, but I think it would be easiest to use a temp table to hold the ids you want to update and use more than one statement. For example:
WITH CTE AS (
SELECT TOP (4) Id
FROM Temp_Runlist AS t1
WHERE (Quantity BETWEEN 1125 AND 1375) AND (Form = 0)
)
SELECT Id INTO #tmpIDs FROM CTE;
GO
IF (SELECT COUNT(Id) FROM #tmpIDs) = 4
BEGIN
UPDATE Temp_Runlist
SET Form = (
SELECT MAX(Form) + 1 AS Form
FROM Temp_Runlist AS t2
)
WHERE Id IN (SELECT Id FROM #tmpIDs)
END;
GO
DROP TABLE #tmpIDs
GO
The CTE gathers the records you want.
The IF checks to see if there are actually 4 records.
Then the UPDATE uses the temp table of Ids to limit.
BTW - your example had MAX(Quantity), when it looks like it should be MAX(Form).

Related

Nested sum loop until foreign key 'dies out'

I am pulling my hair out over a data retrieval function I'm trying to write. In essence this query is meant to SUM up the count of all voorwerpnummers in the Voorwerp_in_Rubriek table, grouped by their rubrieknummer gathered from Rubriek.
After that I want to keep looping through the sum in order to get to their 'top level parent'. Rubriek has a foreign key reference to itself with a 'hoofdrubriek', this would be easier seen as it's parent in a category tree.
This also means they can be nested. A value of 'NULL' in the hoofdcategory column means that it is a top-level parent. The idea behind this query is to SUM up the count of voorwerpnummers in Voorwerp_in_rubriek, and add them together until they are at their 'top level parent'.
As the database and testdata is quite massive I've decided not to add direct code to this question but a link to a dbfiddle instead so there's more structure.
https://dbfiddle.uk/?rdbms=sqlserver_2017&fiddle=8068a52da6a29afffe6dc793398f0998
I got it working in some degree using this query:
SELECT R2.hoofdrubriek ,
COUNT(Vr.rubrieknummer) AS aantal
FROM Rubriek R1
RIGHT OUTER JOIN Rubriek R2 ON R1.rubrieknummer = R2.hoofdrubriek
INNER JOIN Voorwerp_in_rubriek Vr ON R2.rubrieknummer = Vr.rubrieknummer
WHERE NOT EXISTS ( SELECT *
FROM Rubriek
WHERE hoofdrubriek = R2.rubrieknummer )
AND R1.hoofdrubriek IS NOT NULL
GROUP BY Vr.rubrieknummer ,
R2.hoofdrubriek
But that doesn't get back all items and flops in general. I hope someone can help me.
If I got it right
declare #t table (
rubrieknummer int,
cnt int);
INSERT #t(rubrieknummer, cnt)
SELECT R.rubrieknummer, COUNT(Vr.voorwerpnummer)
FROM Rubriek R
INNER JOIN voorwerp_in_rubriek Vr ON R.rubrieknummer = Vr.rubrieknummer
GROUP BY Vr.rubrieknummer, R.rubrieknummer;
--select * from #t;
with t as(
select rubrieknummer, cnt
from #t
union all
select r.hoofdrubriek, cnt
from t
join Rubriek r on t.rubrieknummer = r.rubrieknummer
)
select rubrieknummer, sum(cnt) cnt
from t
group by rubrieknummer;
applying to your fiddle data returns
rubrieknummer cnt
<null> 42
100 42
101 26
102 6
103 10
10000 8
10100 4
10101 1
10102 3
10500 4
10501 2
10502 2
15000 18
15100 6
15101 2
15102 2
15103 2
15500 12
15501 4
15502 3
15503 5
20000 6
20001 2
20002 1
20003 1
20004 2
25000 4
25001 1
25002 1
25003 1
25004 1
30001 2
30002 1
30004 3

Selecting row from group of row based on a column value [duplicate]

This question already has answers here:
Fetch the rows which have the Max value for a column for each distinct value of another column
(35 answers)
Closed 4 years ago.
Have following table
PkId ProfileId Status Amount
1 234 0 10
2 235 1 100
3 236 0 50
4 236 1 80
5 237 0 70
For row number 3 and 4 Profile Id is same but I want in resultset it should consider row having value 0 so whereever we have rows where Profile Id is same it should pick row where status =0 And for rest of the rows it should pick as it is.
Expected Result Set:
PkId ProfileId Status Amount
1 234 0 10
2 235 1 100
3 236 0 50
5 237 0 70
In expected result set row number 4 has to be omitted because :
236 Id at 4 is already there row number 3
Value of Status is 1 in row number 4.
you could use a join on subselect for min status
select * from my_table m
inner join (
select ProfileId , min(Status ) min_status
from my_table
group by ProfileId
) t on t.profileId = m.profileId and t.min_status = m.status

Looping with SQL statements

I am trying to implement a small logic in SQL:
For example : I have two tables A and B
A B
ID Qnt ID Qnt Value
1 50 1 100 1000
2 130 2 200 1000
3 180 3 300 1000
4 320 4 400 2000
5 500 5 500 2000
6 600 2000
7 700 2000
I would to loop through each value of Qnt in TABLE A and check if the value lie between the range of the values in Qnt of TABLE B and get the corresponding value.
I know how I could achieve this with using While loop. But I don't want to do this since looping affects my query performance significantly. I would like to do this with only SQL statements.
Can anyone suggest an idea how I could go with this? just an idea would be great! Any sql would be fine, I would like to know just the logic.
The output would look like :
Output
ID Qnt Value
1 50 1000
2 130 1000
3 180 1000
4 320 2000
5 500 2000
Thanks
This is a lookup. You can do it with a correlated subquery, although the syntax is a bit different in the two databases. Here is the MySQL version:
select a.*,
(select b.value
from b
where b.qnt <= a.qnt
order by b.qnt desc
limit 1
) as value
from a;
Here is the SQL Server version:
select a.*,
(select top 1 b.value
from b
where b.qnt <= a.qnt
order by b.qnt desc
) as value
from a;

SELECT MIN and MAX across fields and aggregate by user

I have the following raw data saved in the db
id min_price, max_price, min_x, max_x, user_id
-------------------------------------------
1 50 200 5 null 1
2 0 100 0 3 1
3 150 300 0 null 1
4 20 200 2 5 2
5 50 200 0 5 2
6 150 200 1 3 2
I want to create a sql query (postgres) with the following data:
min_price, max_price, min_x, max_x, user_id
0 300 0 null 1
20 200 0 5 2
so basically i would get the min and max for each user_id for difference fields, where null should take precedence over the actual max value,
any idea on how to achieve this via sql?
You can check if NULL exists within that column using COUT(*) vs. COUNT(column):
SELECT
user_id,
CASE WHEN COUNT(*) <> COUNT(max_x) THEN NULL ELSE MAX(max_x) END AS max_x
FROM vt
GROUP BY 1
A brute force solution would be:
NULLIF(MAX(COALESCE(max_x, 9999999999)), 9999999999)

How to insert multiple new records based on record(s) already in the table (in Oracle)?

I want to find rows (records) which have a specific vlaue (S) in a column (Type), and insert multiple rows (e.g. 2) based on that row in the same table.
For example, in tabe t1 below, I want for each row of type 'S', 2 rows be inserted with same ID and Price, new Counter value (no specific requirement for this filed, however the Counter for records with same ID must be different), and Type will be 'B'.
It means that when inserting 2 rows based on the first record in table below (1,1200,S,200), Counter value of the new records must be different from Counter values of the records with ID=1 already in the table (1200 and 1201). So, in the initial table there were three records with Type 'S', then in the final table, for each of those records, 2 new records with Type 'B' and a new Counter value is insrted:
ID Counter Type Price
------------------------
1 1200 S 200
1 1201 T 400
2 1200 T 500
3 1546 S 100
3 1547 S 70
4 2607 M 250
The output table t1 will be:
ID Counter Type Price
------------------------
1 1200 S 200
1 1202 B 200
1 1203 B 200
1 1201 T 400
2 1200 T 500
3 1546 S 100
3 1548 B 100
3 1549 B 100
3 1547 S 700
3 1550 B 700
3 1552 B 700
4 2607 M 250
You just have to play twice this command:
insert into epn
with w(max) as
(
select max(t.counter) from t -- useful to get max counter value
)
select t.id, w.max + rownum, 'B', t.price -- max + rownum then gives new values
from t, w
where t.type != 'B'; -- Avoid duplicating rows added 1st time
This gives:
1 1 1200 S 200
2 1 2617 B 200
3 1 2611 B 200
4 1 1201 T 400
5 1 2618 B 400
6 1 2612 B 400
7 2 1200 T 500
8 2 2613 B 500
9 2 2619 B 500
10 3 1547 S 70
11 3 2609 B 70
12 3 2615 B 70
13 3 1546 S 100
14 3 2614 B 100
15 3 2608 B 100
16 4 2607 M 250
17 4 2610 B 250
18 4 2616 B 250
You need an insert select statement:
insert into t1 (ID, Counter, Type, Price)
select ID, Counter+1, 'B', Price from t1 where Type = 'S'
union all
select ID, Counter+2, 'B', Price from t1 where Type = 'S';
EDIT: Here is a statement that matches your criteria mentioned in your remark below. It gets the maximum Counter per ID and adds the count # of the added entry to the ID (1, 2, 3, ...) to it.
insert into t1 (ID, Counter, Type, Price)
select
ID,
(select max(Counter) from t1 where ID = src.ID) + row_number() over (partition by ID order by Price) as new_counter,
'B' as Type,
Price
from
(
select ID, Price
from t1
join (select * from dual union all select * from dual) twice
where t1.Type = 'S'
) src;
CREATE SEQUENCE my_SEQ
INCREMENT BY 1
START WITH 1
MINVALUE 1
MAXVALUE 999999999
NOCYCLE
NOCACHE
NOORDER;
create table MY_TABLE1
( ID NUMBER,
counter number(20),
type varchar2(30),
price number
)
insert into my_table1 (id,counter,type,price)
values (my_SEQ.nextval,1200,'S',200);
insert into my_table1 (id,counter,type,price)
values (my_SEQ.nextval,1300,'B',311);
insert into my_table1 (id,counter,type,price)
values (my_SEQ.nextval,200,'S',110);
insert into my_table1 (id,counter,type,price)
values (my_SEQ.nextval,299,'B',329);
select * from my_table1
ID COUNTER TYPE PRICE
62 1200 S 200
63 1300 B 311
64 200 S 110
65 299 B 329
declare
cursor c1 is select * from My_table1 where type='B';
begin
for rec IN c1
loop
insert into my_table1 (id,counter,type,price)
values (my_SEQ.nextval,rec.counter+1,'Z',rec.price);
end loop;
end;
select * from my_table1
ID COUNTER TYPE PRICE
63 1300 B 311
65 299 B 329
64 200 S 110
62 1200 S 200
66 1301 Z 311
67 300 Z 329
6 rows selected.
So there in cursor select all rows where type is ='B' and insert them back with changed values a little! hope this helps. you could not use sequence but add rec.id+1