Get next rows from selected value in postgresql - postgresql

I want to get next column values from which I selected.
SELECT * FROM test
WHERE round > (
SELECT round from test
WHERE b1 = '9' AND b2 = '16' and b3 = '21'
)
ORDER BY round
LIMIT 1
I tried this sql code but it returns
more than one row returned by a subquery used as an expression error...
SELECT round from test
WHERE b1 = '9' AND b2 = '16' and b3 = '21'
this sql returns multiple rows and I want to get all next rows after this selected rows
if my data is looks like
round b1 b2 b3
1 9 16 21
2 2 13 24
3 9 16 21
4 5 14 22
I want to get
(2,2,13,24)(4,5,14,22)
Can I get help?? Thanks

demo:db<>fiddle
You can use lead() window function which puts the next value to the current record:
SELECT
next_round, next_b1, next_b2, next_b3
FROM (
SELECT
*,
lead(round) OVER w as next_round,
lead(b1) OVER w as next_b1,
lead(b2) OVER w as next_b2,
lead(b3) OVER w as next_b3
FROM t
WINDOW w AS (ORDER BY round)
) s
WHERE b1 = 9 AND b2 = 16 AND b3 = 21

Simplest way is to use (NOT) EXISTS():
SELECT *
FROM test t
WHERE NOT EXISTS (
SELECT round from test nx
WHERE nx.b1 = '9' AND nx.b2 = '16' and nx.b3 = '21'
AND nx.round >= t.round
);
This will retrieve all rows if no matching record(s) exist.

You can do it with a self join:
SELECT DISTINCT t1.*
FROM test t1 INNER JOIN test t2
ON t1.round > t2.round AND (t1.b1, t1.b2, t1.b3) <> (t2.b1, t2.b2, t2.b3)
WHERE (t2.b1, t2.b2, t2.b3) = (9, 16, 21)
ORDER BY t1.round
See the demo.
Results:
round
b1
b2
b3
2
2
13
24
4
5
14
22

Related

postgreSQL and joining on alias

I have a query that, in very simplified form, looks like this:
SELECT aa, bb, xx
FROM (VALUES ('a1', 'b1'), ('a2', 'b2')) as T1(aa, bb)
LEFT JOIN (
SELECT xx FROM (VALUES
('a1', 'y1'), ('x2', 'y2')) as T2(xx, yy)
) as T3 ON T1.aa = T3.xx ;
which executes to produce this:
aa | bb | xx
----+----+--------
a1 | b1 | a1
a2 | b2 | (NULL)
(2 rows)
In real life the column xx is defined using a case statement and alias:
SELECT
...,
(CASE WHEN rgr.age_years < 18 THEN '< 18'
WHEN rgr.age_years < 36 THEN '26-35'
WHEN rgr.age_years < 46 THEN '36-45'
WHEN rgr.age_years < 56 THEN '46-55'
WHEN rgr.age_years < 130 THEN '> 55' END)
as row_name,
...
FROM
(VALUES ('< 18'), ('26-35'), ('36-45'),
('46-55'), ('> 55'))
as prn(possible_row_name)
LEFT JOIN other_table rgr
ON prn.possible_row_name = ??row_name??
Obviously ??row_name?? is an alias and so is not available at the time I specify the join. But I've not found the right formulation.
The point is simply that I'm doing a select on table rgr and I want all the age tranches present, even if no entity satisfies that number.
You can create the column on the fly inside the table expression rgr, as in:
SELECT
...,
rgr.row_name,
...
FROM
(VALUES ('< 18'), ('26-35'), ('36-45'),
('46-55'), ('> 55'))
as prn(possible_row_name)
LEFT JOIN ( -- table expression rgr defined here
select t.*,
CASE WHEN rgr.age_years < 18 THEN '< 18'
WHEN rgr.age_years < 36 THEN '26-35'
WHEN rgr.age_years < 46 THEN '36-45'
WHEN rgr.age_years < 56 THEN '46-55'
WHEN rgr.age_years < 130 THEN '> 55' END
as row_name
from other_table t
) rgr ON prn.possible_row_name = rgr.row_name

Pivoting results from CTE in Postgres

I have a large SQL statements(PostgreSQL version 11) with many CTE's, i want to use the results from an intermediary CTE to create a PIVOTed set of results and join it with other CTE.
Below is a small part of my query and the CTE "previous_months_actual_sales" is the one i need to PIVOT.
,last_24 as
(
SELECT l_24m::DATE + (interval '1' month * generate_series(0,24)) as last_24m
FROM last_24_month_start LIMIT 24
)
,previous_months_actual_sales as
(
SELECT TO_CHAR(created_at,'YYYY-MM') as dates
,b.code,SUM(quantity) as qty
FROM base b
INNER JOIN products_sold ps ON ps.code=b.code
WHERE TO_CHAR(created_at,'YYYY-MM')
IN(SELECT TO_CHAR(last_24m,'YYYY-MM') FROM last_24)
GROUP BY b.code,TO_CHAR(created_at,'YYYY-MM')
)
SELECT * FROM previous_months_actual_sales
The results of this CTE "previous_months_actual_sales" is shown below,
dates code qty
"2018-04" "0009" 23
"2018-05" "0009" 77
"2018-06" "0008" 44
"2018-07" "0008" 1
"2018-08" "0009" 89
The expected output based on the above result is,
code. 2018-04. 2018-05. 2018-06. 2018-07. 2018-08
"0009". 23 77 89
"0008". 44 1
Is there a way to achieve this?

Oracle 10g to know the timedifference between two rows in one table

requestid Date
2 12/22/2016 23:21
3 12/22/2016 23:21
1 12/22/2016 23:21
37 12/22/2016 23:20
156 12/22/2016 23:20
Could someone please help I want to know the time Difference between requestid 1 and 156?
I have tried the below query but not getting the proper output.
Select A.IFCOMPONENTUID,A.FPROCSTAGESTARTDT, (A.FPROCSTAGESTARTDT - B.FPROCSTAGESTARTDT) AS timedifference from XA_CASA.CFX_FILE_PROC_STAT A
where INNER JOIN XA_CASA.CFX_FILE_PROC_STAT B On A.IFCOMPONENTUID = (B.IFCOMPONENTUID + 155) and
order by FILERUNTIMEUID desc
similar to your construct...
SELECT
A.IFCOMPONENTUID,
A.FPROCSTAGESTARTDT,
B.IFCOMPONENTUID CompareID
(A.FPROCSTAGESTARTDT - B.FPROCSTAGESTARTDT) TimeDiff
FROM XA_CASA.CFX_FILE_PROC_STAT A
INNER JOIN XA_CASA.CFX_FILE_PROC_STAT B
ON A.IFCOMPONENTUID = (B.IFCOMPONENTUID + 155)
WHERE A.IFCOMPONENTUID = 1
I would prefer having explicit IDs in the where clause for more flexibility to change IDs:
SELECT
A.IFCOMPONENTUID,
A.FPROCSTAGESTARTDT,
B.IFCOMPONENTUID CompareID,
(A.FPROCSTAGESTARTDT - B.FPROCSTAGESTARTDT) TimeDiff
FROM XA_CASA.CFX_FILE_PROC_STAT A
INNER JOIN XA_CASA.CFX_FILE_PROC_STAT B
ON 1 = 1
WHERE A.IFCOMPONENTUID = 1
AND B.IFCOMPONENTUID = 156

PostgreSQL non-overlapping ranges

I use PostgreSQL database and have a cards table.
Each record(card) in this table have card_drop_rate integer value.
For example:
id | card_name |card_drop_rate
-------------------------------
1 |card1 |34
2 |card2 |16
3 |card3 |54
max drop rate is 34 + 16 + 54 = 104.
In accordance to my application logic I need to find a random value between 0 and 104 and then retrieve card according to this number, for example:
random value: 71
card1 range: 0 - 34(0 + 34)
card2 range: 34 - 50(34 + 16)
card3 range: 50 - 104(50 + 54)
So, my card is card3 because 71 is placed in the range 50 - 104
What is the proper way to reflect this structure in PostgreSQL ? I'll need to query this data often under so the performance is a criterion number one for this solution.
Following query works fine:
SELECT
b.id,
b.card_drop_rate
FROM (SELECT a.id, sum(a.card_drop_rate) OVER(ORDER BY id) - a.card_drop_rate as rate, card_drop_rate FROM cards as a) b
WHERE b.rate < 299 ORDER BY id DESC LIMIT 1
You can do this using cumulative sums and random. The "+ 1"s might be throwing me off, but it is something like this:
with c as (
select c.*,
sum(card_drop_rate + 1) - card_drop_rate as threshhold
from cards c
),
r as (
select random() * (sum(card_drop_rate) + count(*) - 1) as which_card
from cards c
)
select c.*
from c cross join
r
where which_card >= threshhold
order by threshhold
limit 1;
For performance, I would simply take the cards and generate a new table with 106 slots. Assign the card value to the slots and build an index on the slot number. Then get a value using:
select s.*
from slots s
where s.slotid = floor(random() * 107);

SQL - Select max week from a group

I need to be able to get a result set which shows the last teacher for a course, for which I have the following SQL query:
SELECT
a.acad_period, MAX(a.start_week) as start_week,
a.staff_code, b.aos_code, b.aos_period
FROM
qlsdat.dbo.sttstaff a
INNER JOIN
qlsdat..sttrgaos b ON a.acad_period = b.acad_period
AND a.register_id = b.register_id
AND a.register_group = b.register_group
WHERE
a.acad_period = '14/15'
GROUP BY
a.acad_period, a.staff_code, b.aos_code, b.aos_period
However, the issue is that it returns to me the maximum start week for a teacher on that course, whereas I want the maximum start week for a course, and the teacher that happens to be teaching for that start week.
Here is a sample result set returned from the above query:
14/15 37 HKARUNATHIL A2ES 001A
14/15 37 CSHUKLA A2ES 001B
14/15 37 PSEDOV A2ES 002A
14/15 37 BBANFIELD A2ES 002B
14/15 14 VKRISHNASWA A2EX BL1 X
14/15 14 VKRISHNASWA A2EX BL2 X
14/15 6 BODAMEKENTO ACA2 BL1 A
14/15 41 SKLER ACA2 BL1 A
14/15 44 BODAMEKENTO ACAS BL1 F
14/15 37 MMILLER ARA2 BL1 C
14/15 45 MMILLER ARAS BL1 E
14/15 44 SHOULTON ARAS BL1 E
Here is an example of the problem within the result set:
14/15 10 HMALIK MMGX GB2F3
14/15 44 JMULLANEY MMGX GB2F3
In the above example I only want:
14/15 44 JMULLANEY MMGX GB2F3
The query produced is going to be used as a subquery in another query.
This will get the row for highest start_week, however you may encounter some problems if you have data from more than 1 year, this can be resolved by putting your your field in addition to your week column in this part
row_number() over (partition by
a.acad_period, b.aos_code, b.aos_period
order by
a.start_year desc,
a.start_date desc) rn
Query:
;WITH CTE AS
(
SELECT
a.acad_period, a.start_week,
a.staff_code, b.aos_code, b.aos_period,
row_number() over (partition by
a.acad_period, b.aos_code,
b.aos_period
order by a.start_week desc) rn
FROM
qlsdat.dbo.sttstaff a
INNER JOIN
qlsdat..sttrgaos b ON a.acad_period = b.acad_period
AND a.register_id = b.register_id
AND a.register_group = b.register_group
WHERE
a.acad_period = '14/15'
)
SELECT
acad_period, start_week,
staff_code, aos_code, aos_period,
FROM CTE
WHERE rn = 1