Postgres select work 3x time faster then function with that select - postgresql

I have a SELECT in Postgres:
SELECT DISTINCT ON (price) price, quantity, is_ask, final_update_id
FROM (SELECT *
FROM ((SELECT price, quantity, is_ask, book_depth.final_update_id
FROM order_depth
LEFT JOIN book_depth ON book_depth_id = book_depth.id
WHERE book_depth_id IN (SELECT id
FROM book_depth
WHERE final_update_id > (SELECT last_update_id
FROM order_book
WHERE symbol_name = 'XRPRUB'
ORDER BY last_update_id DESC
LIMIT 1)
AND symbol_name = 'XRPRUB'))
UNION
(SELECT price, quantity, is_ask, order_book_id
FROM "order"
WHERE order_book_id = (SELECT id
FROM order_book
WHERE symbol_name = 'XRPRUB'
ORDER BY last_update_id DESC
LIMIT 1))
ORDER BY final_update_id DESC) AS t) AS t1
ORDER BY price, final_update_id DESC;
It works for about 20 seconds.
But when I create function with this select this function works for about 1 min 40 seconds. Can someone explain me is it normal or I make mistake somewhere?

Related

extracting records from rank = 1

I would like to get name of title that have the number 1 in the rank column.
SELECT title, RANK() OVER(ORDER BY COUNT(*) DESC) rank
FROM rentals as w join copies as e on w.signature = e.signature join books as c on e.idbook = c.idbook
WHERE dateofloan <= CURRENT_DATE - 31
GROUP BY title;
My code shows two columns
title, rank
Thank you in advance for your help.
Subquery and restrict to the first rank:
WITH cte AS (
SELECT title, RANK() OVER (ORDER BY COUNT(*) DESC) rnk
FROM rentals w
INNER JOIN copies e ON w.signature = e.signature
INNER JOIN books c ON e.idbook = c.idbook
WHERE dateofloan <= CURRENT_DATE - 31
GROUP BY title
)
SELECT title
FROM cte
WHERE rnk = 1;

How to select from relationship based on the height value of field postgres?

i have three tables vehicles and trips and componentValues they are related to each other by
vehicles -> trips -> componentValues
vehicles Table : id, ...
trips Table: id, vehicle_id, ...
componentValues Table: id, trip_id, damage, ...
and i'm trying to get all the trips with the highest damage component form the componentValues table like this
SELECT *
FROM (select * from trips WHERE "trips"."vehicle_id" = '7') as t
LEFT JOIN (
select * from "component_values"
where trip_id = '85'
order by damage
desc nulls last limit 1
) as h on t.id = h.trip_id
how can i change the line where trip_id = '85' to be dynamic or is their another way to do this and many thanks in advance.
expected result:
UPDATE
i have did some query that get what i want but how can i improve it by not using sub queries in the select statement
select * ,
(select damage from "component_values" where trip_id = trips.id order by damage desc nulls last limit 1) as h_damage,
(select damage from "component_values" where trip_id = trips.id order by damage asc nulls first limit 1) as l_damage,
(select component_types.name from "component_values" left join component_types on component_values.component_type_id = component_types.id where trip_id = trips.id order by damage desc nulls last limit 1) as hc_damage,
(select component_types.name from "component_values" left join component_types on component_values.component_type_id = component_types.id where trip_id = trips.id order by damage asc nulls first limit 1) as lc_damage
from trips
WHERE trips."vehicle_id" = '7'
I think you want distinct on:
select distinct on (t.id) t.*, dv.damage
from trips t join
component_values cv
on cv.trip_id = t.id
where t.vehicle_id = 7 -- not sure if this is needed
order by t.id, cv.damage desc nulls last;
distinct on is usually the most efficient method in Postgres. You can also do this with window functions:
select distinct on (t.id) t.*, cv.damage
from trips t join
(select cv.*,
row_number() over (partition by cv.trip_id, cv.damage desc nulls last) as seqnum
from component_values cv
) cv
on cv.trip_id = t.id and cv.seqnum = 1
where t.vehicle_id = 7; -- not sure if this is needed
I think you want a lateral join.
SELECT *
FROM (select * from trips WHERE "trips"."vehicle_id" = '7') as t
LEFT JOIN lateral (
select * from "component_values"
where trip_id = t.id
order by damage
desc nulls last limit 1
) as h on true
Although I don't think there is a reason for the first subquery, so:
SELECT *
FROM trips
LEFT JOIN lateral (
select * from "component_values"
where trip_id = trips.id
order by damage
desc nulls last limit 1
) as h on true
WHERE "trips"."vehicle_id" = '7'

Select specific lines in data according to last update [duplicate]

Name Value AnotherColumn
-----------
Pump 1 8000.0 Something1
Pump 1 10000.0 Something2
Pump 1 10000.0 Something3
Pump 2 3043 Something4
Pump 2 4594 Something5
Pump 2 6165 Something6
My table looks something like this. I would like to know how to select max value for each pump.
select a.name, value from out_pumptable as a,
(select name, max(value) as value from out_pumptable where group by posnumber)g where and g.value = value
this code does the job, but i get two entries of Pump 1 since it has two entries with same value.
select name, max(value)
from out_pumptable
group by name
select name, value
from( select name, value, ROW_NUMBER() OVER(PARTITION BY name ORDER BY value desc) as rn
from out_pumptable ) as a
where rn = 1
SELECT
b.name,
MAX(b.value) as MaxValue,
MAX(b.Anothercolumn) as AnotherColumn
FROM out_pumptabl
INNER JOIN (SELECT
name,
MAX(value) as MaxValue
FROM out_pumptabl
GROUP BY Name) a ON
a.name = b.name AND a.maxValue = b.value
GROUP BY b.Name
Note this would be far easier if you had a primary key. Here is an Example
SELECT * FROM out_pumptabl c
WHERE PK in
(SELECT
MAX(PK) as MaxPK
FROM out_pumptabl b
INNER JOIN (SELECT
name,
MAX(value) as MaxValue
FROM out_pumptabl
GROUP BY Name) a ON
a.name = b.name AND a.maxValue = b.value)
select Name, Value, AnotherColumn
from out_pumptable
where Value =
(
select Max(Value)
from out_pumptable as f where f.Name=out_pumptable.Name
)
group by Name, Value, AnotherColumn
Try like this, It works.
select * from (select * from table order by value desc limit 999999999) v group by v.name
Using analytic function is the easy way to find max value of every group.
Documentation : https://learn.microsoft.com/en-us/sql/t-sql/functions/row-number-transact-sql?view=sql-server-ver15
Select name,
value,
AnotherColumn
From(
SELECT Row_Number() over(partition by name order by value desc)as
row_number, *
FROM students
)
Where row_number = 1
SELECT t1.name, t1.Value, t1.AnotherColumn
FROM mytable t1
JOIN (SELECT name AS nameMax, MAX(Value) as valueMax
FROM mytable
GROUP BY name) AS t2
ON t2.nameMax = t1.name AND t2.valueMax = t1.Value
WHERE 1 OR <anything you would like>
GROUP BY t1.name;
SELECT DISTINCT (t1.ProdId), t1.Quantity FROM Dummy t1 INNER JOIN
(SELECT ProdId, MAX(Quantity) as MaxQuantity FROM Dummy GROUP BY ProdId) t2
ON t1.ProdId = t2.ProdId
AND t1.Quantity = t2.MaxQuantity
ORDER BY t1.ProdId
this will give you the idea.

Querying Records Based on Key & MAX Date Column

I am hoping I can get some clarification on how to best handle getting the data set correct and efficiently.
Here are three queries from three different tables I am working with. The Donor_ID is key between the tables, but as you can see - there are multiple records associated with each Donor_ID - with the runid_gmt column having differing dates.
Ideally, I would like use the max(runid_gmt) for each record - and join the EMAIL and ADDRESSES tables on the Donor_ID but only select the max(runid_gmt) record in each of those tables as well.
I believe that is what I need to do - but not sure. Any suggestions on how to tackle this problem?
SELECT donor_id, last_name, birthdate, runid_gmt
FROM [dbo].TBL_DONORS where donor_id = '51999441' order by runid_gmt desc;
SELECT donor_id, city, state, zip, runid_gmt
FROM [dbo].TBL_ADDRESSES where donor_id = '51999441' order by runid_gmt desc;
SELECT donor_id, donor_email, runid_gmt
FROM [dbo].TBL_EMAIL where donor_id = '51999441' order by runid_gmt desc;
Try with row_number window function:
select * from
(select *, row_number() over(partition by donorid order by gmt desc) rn
from donors) t1 join
(select *, row_number() over(partition by donorid order by gmt desc) rn
from addresses) t2 on t1.donorid = t2.donorid join
(select *, row_number() over(partition by donorid order by gmt desc) rn
from emails) t3 on t1.donorid = t3.donorid
where t1.rn = 1 and t2.rn = 1 and t3.rn = 1

Lead() considering where clause, don't want it to

I have a simple log table named TaskLog. I want to see how long a certain step is taking by looking at the time difference between that step and the one after.
The query below has the effect of looking at the time between rows that read 'performing media relations rollup' - I don't want the LEAD() rows to follow the WHERE clause. (they should take the next row in cteTask regardless of the value of Notes.
;with cteTask as
(select * from Tasklog where prog = 'StatsMajor' and Date>'8/1/2014')
, cteLead as
(select *
, LEAD(ID) over (order by ID) NextID
, LEAD(date) over (order by ID) NextDt
from cteTask
where notes = 'performing media relations rollup'
)
select *, DATEDIFF(second, Date, NextDt) as Secs
from cteLead
Just filter after?
;with cteTask as
(select * from Tasklog where prog = 'StatsMajor' and Date>'8/1/2014')
, cteLead as
(select *
, LEAD(ID) over (order by ID) NextID
, LEAD(date) over (order by ID) NextDt
from cteTask
)
select *, DATEDIFF(second, Date, NextDt) as Secs
from cteLead
where notes = 'performing media relations rollup'