Postgres, get distinct field that has value as specified - sql

Please see table below:
id ticket_id event
1 130 response
2 130 query
3 130 create
4 130 update
5 131 response
6 131 query
7 131 create
8 132 response
9 132 query
10 132 create
How do i return distinct ticket_id that has no 'update' (value) on event field in a postgres sql
example it should return ticket_id 131 and 132
Thanks.

You can use a NOT EXISTS condition:
select distinct t1.ticket_id
from the_table t1
where not exists (select *
from the_table t2
where t2.ticket_id = t1.ticket_id
and t2.event = 'update')

Related

Select query with same value in row

I have a table structure like this:
order_item_id order_id product_id
1 513 120
2 213 121
3 513 120
4 312 131
5 312 131
6 102 123
I want to have a SQL query where I can get the following results:
order_item_id order_id product_id
1 513 120
3 513 120
4 312 131
5 312 131
I used the following SQL query to fetch the results, but it doesn't help:
SELECT *
FROM `stg_83087_wc_order_product_lookup`
WHERE `order_id` = `order_id` and `product_id` = `product_id`
The only question I have to is to get the next value of the row so I can make the comparison here.
You basically need to check the existance of similar rows:
select *
from t
where exists (
select * from t t2
where t2.order_id = t.order_id
and t2.product_id = t.product_id
and t2.order_item_id != t.order_item_id
);

How to fetch records up to particular data

Table tb data as below
id remarks status key
------------------------------------
1 dfe 1 340
2 ert 4 340
3 aaa 6 340
4 gh 7 340
5 bnh 2 341
6 xdc 6 341
7 qqq 1 341
8 rty 3 343
9 mnh 1 343
10 ppo 3 343
11 oit 6 343
I want to get list of id upto status 6 by grouping key.
select id
from tb
where status in (6) AND key in (select key_list from temporary_table)
group by key;
I didnt get required records.
For example: I need output as below for
key 340
id
---
1
2
3
For key 341
id
---
5
6
For key 343
id
---
8
9
10
11
Note: Orginal key column data not in order, it is in suffle. For easy understanding in given list this.
You can use a correlated subquery:
select tb.* -- or whatever columns you want
from tb
where tb.id <= (select min(tb2.id)
from tb tb2
where tb2.key = tb.key and tb2.status = 6
);
Alternatively, you can use a window function:
select tb.id
from (select tb.*,
min(case when status = 6 then id end) over (partition by key) as id_6
from tb
) tb
where id <= id_6;
Assuming that
You need a query to return the respective id's for all keys in a
single query;
If for a given key the status is never 6, then you must return all
the rows for that key
you can do this with match_recognize:
select key, id
from tb
match_recognize(
partition by key
order by id
all rows per match
pattern ( ^ a* b? )
define a as status != 6
);
If, instead, you must give the query a single input key as a variable, and return the result just for that key, you can modify the query like this:
select id
from (select id, status from tb where key = :input_key)
match_recognize(
order by id
all rows per match
pattern ( ^ a* b? )
define a as status != 6
);

SQL Update where select top(num)

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).

How to select group by 2 columns + id?

I have a problem with data selection using SQL in PostgreSQL database.
I have the following data in one table:
ID ID_X ID_Y
100 1 2
101 1 1
102 1 1
103 1 2
104 5 10
105 5 11
106 5 10
107 5 11
108 8 20
109 8 30
110 8 20
How to write select statement to get the following results?
ID ID_X ID_Y
100 1 2
101 1 1
104 5 10
105 5 11
108 8 20
109 8 30
I know that it is a kind of group by ID_X and ID_Y but how to select also "ID" column without grouping by it?
Maybe there is a way to select using distinct? or group by with subselect? Please help :)
You can use an aggregate function like MIN() or MAX(). In your case you want MIN() to get those specific results.
SELECT MIN(ID), ID_X, ID_Y
FROM [tablename]
GROUP BY ID_X, ID_Y
Try this using Distinct on
select *
from
(
select distinct on (id_x, id_y) ID, id_x, id_y
FROM t order by id_x, id_y,id
) q
order by id
Seems like you want a GROUP BY. Use MIN() to return each group's lowest ID:
select min(ID), ID_X, ID_Y
from tablename
group by ID_X, ID_Y
Alternatively, you can do a NOT EXISTS:
select *
from tablename t1
where not exists (select 1 from tablename t2
where t2.ID_X = t1.ID_X
and t2.ID_Y = t1.ID_Y
and t2.ID < t1.ID)
I.e. return a row as long as there are no (other) row with same ID_X and ID_Y but a lower ID.

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