SQL Improvement in PostgreSQL - sql

I have two tables (Order, OrderStatus) as below. Order.OrderCode and OrderStatus.Status are indexed.
Order table:
Id(PK),OrderCode
1,"AA-001"
2,"AA-001"
3,"AA-002"
4,"AA-002"
5,"AA-003"
OrderStatus table:
Id(PK),OrderId(FK),Status,CreatedAt
1,1,"Open",2021-05-01 13:00:00
2,1,"Close",2021-05-01 13:05:00
3,2,"Open",2021-05-01 13:10:01
4,3,"Open",2021-05-01 13:10:02
5,3,"Close",2021-05-01 13:11:00
6,4,"Open",2021-05-01 13:11:01
7,4,"Close",2021-05-01 13:11:05
8,5,"Open",2021-05-01 13:12:00
I have a query to return the total Order count for any OrderCode group which has a latest Open status.
For example OrderCode AA-001 and OrderCode AA-003 have Order (ID:2,5) who have latest Open status. The query result would be 3.
OrderCode,Count
"AA-001",2
"AA-003",1
This is the query I have now. The inner query in the IN condition is basically to find Order record whose latest Status is Open. Is there any other way to improve this query? Thanks.
SELECT COUNT(1)
FROM Order as ord
WHERE ord.OrderCode in (
SELECT distinct(or.OrderCode)
from Order as or
left JOIN (
SELECT DISTINCT ON (OrderId) *
FROM OrderStatus
ORDER BY OrderId, CreatedAt DESC
) orsts ON orsts.OrderId = or.Id
where orsts.Status = 'Open'
)

You seem to be describing:
SELECT COUNT(DISTINCT o.OrderCode)
FROM Order o JOIN
(SELECT DISTINCT ON (os.OrderId) *
FROM OrderStatus os
ORDER BY os.OrderId, os.CreatedAt DESC
) orsts
ON orsts.OrderId = o.Id
WHERE orsts.Status = 'Open';
For performance, you want an index on OrderStatus(OrderId, CreatedAt DESC).

Related

Using rownum in sub query on oracle 11

I have query like this.It is working properly on oracle 12.
select * from customers where customerId IN (select custId from Orders where
orderStatus = 'S' and orderCity = 'Amsterdam' and ORDER BY custId DESC FETCH FIRST 10 ROWS ONLY)
But I am using oracle 11.That query is not working on oracle 11.Therefore I changed my query like that
select * from customers where customerId IN (select custId from Orders where
orderStatus = 'S' and orderCity = 'Amsterdam' and ORDER BY custId DESC rownum <= 10)
It gives missing right paranthesis
How can I solve this problem.Do you have any idea?Actually I use a variable instead on 10 number.
Syntax is a bit different - you need an extra subquery so it would need to be more like ...
where customerId IN (select * from (select custId from Orders where orderStatus = 'S' and orderCity = 'Amsterdam' and ORDER BY custId DESC) where rownum <=10)
You wouldn't need the extra subquery if you didn't have the order by clause
For ref see https://docs.oracle.com/cd/B19306_01/server.102/b14200/pseudocolumns009.htm#:~:text=For%20each%20row%20returned%20by,has%202%2C%20and%20so%20on.
I would phrase this with exists and row_number():
select c.*
from customers
where exists (
select 1
from (
select custId, row_number() over(order by custid desc) rn
from orders
where orderStatus = 'S' and orderCity = 'Amsterdam'
) o
where o.rn <= 10 and o.cusid = c.customerid
)
row_number() ranks orders by descending customer id in the subquery. Then, we filter on the first 10 rows, and use exists to filter the corresponding rows in the outer query.

SQLite query with LIMIT per column

I am trying to compose a query with a where condition to get multiple unique sorted columns without having to do it in multiple queries. That is confusing so here is an example...
Price Table
id | item_id | date | price
I want to query to find the most recent price of multiple items given a date. I was previously iterating through items in my application code and getting the most recent price like this...
SELECT * FROM prices WHERE item_id = ? AND date(date) < date(?) ORDER BY date(date) DESC LIMIT 1
Iterating through each item and doing a query is too slow so I am wondering if there is a way I can accomplish this same query for multiple items in one go. I have tried UNION but I cannot get it to work with the ORDER BY and LIMIT commands like this thread says (https://stackoverflow.com/a/1415380/4400804) for MySQL
Any ideas on how I can accomplish this?
Try this (based on adapting the answer):
SELECT * FROM prices a WHERE a.RowId IN (
SELECT b.RowId
FROM prices b
WHERE a.item_id = b.item_id AND date < ?
ORDER BY b.item_id LIMIT 1
) ORDER BY date DESC;
Window functions (Available with sqlite 3.25 and newer) will likely help:
WITH ranked AS
(SELECT id, item_id, date, price
, row_number() OVER (PARTITION BY item_id ORDER BY date DESC) AS rn
FROM prices
WHERE date < ?)
SELECT id, item_id, date, price
FROM ranked
WHERE rn = 1
ORDER BY item_id;
will return the most recent of each item_id from all records older than a given date.
I would simply use a correlated subquery in the `where` clause:
SELECT p.*
FROM prices p
WHERE p.DATE = (SELECT MAX(p2.date)
FROM prices p2
WHERE p2.item_id = p.item_id
);
This is phrase so it works on all items. You can, of course, add filtering conditions (in the outer query) for a given set of items.
With NOT EXISTS:
SELECT p.* FROM prices p
WHERE NOT EXISTS (
SELECT 1 FROM prices
WHERE item_id = p.item_id AND date > p.date
)
or with a join of the table to a query that returns the last date for each item_id:
SELECT p.*
FROM prices p INNER JOIN (
SELECT item_id, MAX(date) date
FROM prices
GROUP BY item_id
) t ON t.item_id = p.item_id AND t.date = p.date

Remove duplicate records based on timestamp

I'm writing a query to find duplicate records. I have table with following columns
Id, Deliveries, TankId, Timestamp.
I have inserted duplicate records, that is for same tankid, same deliveries with the +1 day offset timestamp.
Now I want to remove duplicate records which is with lesser timestamp.
e.g. I have duplicate deliveries added for same tankid on 24th and 25th july. I need to remove 24th record.
I tried the following query;
SELECT raw.TimeStamp,raw.[Delivery],raw.[TankId]
FROM [dbo].[tObservationData] raw
INNER JOIN (
SELECT [Delivery],[TankSystemId]
FROM [dbo].[ObservationData]
GROUP BY [Delivery],[TankSystemId]
HAVING COUNT([ObservationDataId]) > 1
) dup
ON raw.[Delivery] = dup.[Delivery] AND raw.[TankId] = dup.[TankId]
AND raw.TimeStamp >'2019-06-30 00:00:00.0000000' AND raw.[DeliveryL]>0
ORDER BY [TankSystemId],TimeStamp
But above gives other records too, how can I find and delete those duplicate records?
In this case you can use partition by order by clause. You can partition by TankID and Delivery and order by Timestamp in desc order
Select * from (
Select *,ROW_NUMBER() OVER (PARTITION BY TankID,Delievry ORDER BY [Timestamp] DESC) AS rn
from [dbo].[ObservationData]
)
where rn = 1
In the above code records with rn=1 will have the latest timestamp. So you can only select those and ignore others. Also you can use the same to remove/delete the records from you table.
WITH TempObservationdata (TankID,Delivery,Timestamp)
AS
(
SELECT TankID,Delivery,ROW_NUMBER() OVER(PARTITION by TankID, Delivery ORDER BY Timsetamp desc)
AS Timestamp
FROM dbo.ObservationData
)
--Now Delete Duplicate Rows
DELETE FROM TempObservationdata
WHERE Timestamp > 1
think it will work
SELECT raw.TimeStamp,raw.[Delivery],raw.[TankId]
FROM [dbo].[tObservationData] raw
INNER JOIN (
SELECT [Delivery],[TankSystemId],min([TimeStamp]) as min_ts
FROM [dbo].[ObservationData]
GROUP BY [Delivery],[TankSystemId]
HAVING COUNT([ObservationDataId]) > 1
) dup
ON raw.[Delivery] = dup.[Delivery] AND raw.[TankId] = dup.[TankId] and raw.[TimeStamp] = dup.min_ts
AND raw.TimeStamp >'2019-06-30 00:00:00.0000000' AND raw.[DeliveryL]>0
ORDER BY [TankSystemId],TimeStamp
Are you just looking for this?
SELECT od.*
FROM (SELECT od.*,
ROW_NUMBER() OVER (PARTITION BY od.TankId, od.Delivery ORDER BY od.TimeStamp DESC) as seqnum
FROM [dbo].[tObservationData] od
) od
WHERE seqnum = 1;

Select customer with highest price of all his orders

i want to list customer id with highest sum of price of his orders. Please see graph oí orders down.
SQL DEMO
SELECT *
FROM (
SELECT "customerid", SUM("price")
FROM Orders
GROUP BY "customerid"
ORDER BY SUM("price") DESC
) T
WHERE ROWNUM <= 1
You need to group by customer_id to get all prices of each customers.
Then sum these prices filter it with max( sum(price))or get first row by descending order of sum(price).
--for Oracle
select * from (Select c.name,c.id,sum(o.price) from Customer c
inner join order o on o.customer_id=c.id
group by c.name,c.id
order by sum(o.price)desc
)where rownum =1
--For sql server and mysql
Select top 1 c.name,c.id,sum(o.price) from Customer c
inner join order o on o.customer_id=c.id
group by c.name,c.id
order by sum(o.price)desc

What's the best way to return the most recent row of data?

So, I have a large stored procedure I'm working on. I'm trying to add query to it that give me the most recent customer order of a particular product.
The trouble is there's nothing that makes the row unique other than the date. I know I need to grab all of the order for the product, including the order date, then ORDER BY OrderDate DESC —the top result is what I need, I'm running into problems isolating the row.
My query looks something like this (the temp table is part of a long series of temp tables in the query:
SELECT t5.*, co.OrderName, co.OrderDate
FROM #TempTable5 t5
JOIN #CustomerOrders co
ON t5.CustomerGUID = co.CustomerGUID
WHERE co.OrderSet = 'Product25'
This produces the results I need, but obviously with all of the products order. What's the most efficient way to just grab the most recent order?
Thanks!
if you have an id_column on the table which has an auto increment, you could put on the query :
select * from table order by id_column DESC LIMIT 1
You can use row_number as below:
Select * from (
SELECT t5.*, co.OrderName, co.OrderDate,
RowN = Row_number() over(partition by OrderSet order by OrderDate Desc) --Here If OrderSet is not product provide product column here
FROM #TempTable5 t5
JOIN #CustomerOrders co
ON t5.CustomerGUID = co.CustomerGUID
WHERE co.OrderSet = 'Product25'
) a Where a.RowN = 1
Try this.
SELECT t5.*, co.OrderName, co.OrderDate
FROM #TempTable5 t5 JOIN #CustomerOrders co ON t5.CustomerGUID = co.CustomerGUID
WHERE co.OrderSet = 'Product25' AND co.OrderDate = (SELECT TOP(1) OrderDate FROM #CustomerOrders WHERE CustomerGUID = t5.CustomerGUID ORDER BY OrderDate DESC)