Select MAX for multiple criteria in a group - sql

Apologies if this has been answered, I'm new enough that I didn't even know how to search:
I have one table:
Lot SKU Cost Date
1001-1 1001 .30 10-12-14
1001-2 1001 .33 10-19-14
1001-3 1001 .32 11-20-14
1002-1 1002 .45 10-12-14
1002-2 1002 .45 10-19-14
1002-3 1002 .44 12-01-14
1003-1 1003 .12 10-15-14
1003-2 1003 .13 10-19-14
1003-3 1003 .10 11-23-14
i need to sum the cost of the oldest row for each SKU.
expected outcome: (.30 + .45 + .12) = .87
is this possible through one query?

ANSI SQL support a function called row_number(), which can be very helpful for this type of query. The following is how you would use it in this case:
select sum(cost)
from (select t.*, row_number() over (partition by sku order by date) as seqnum
from table t
) t
where seqnum = 1;

This should work:
select sum(t.cost)
from some_table t
join ( select sku ,
min(some_datetime_column) oldest
from some_table
and some_datetime_column is not null
group by sku
) s on s.sku = t.sku
and s.oldest = t.some_datetime_column

Related

How to calculate +ve and -ve amounts on Totals in SQL?

The below Product table, Product ID - 100 as duplicated twice, and also there are negative profits are needs to Substract while calculating the Profit wise Total.
PID | Pname | Profit
100 AB 20
100 AB 20
101 BC 30
102 CD -10
103 DE -10
Expected Result: 30
Please provide the SQL query to get this result. Thanks in advance!!!
Is this what you want?
select sum(profit)
from (select distinct t.*
from t
) t
WITH CTE AS (
SELECT ROW_NUMBER() OVER (PARTITION BY PID ORDER BY PID ) AS rn,
PID,Pname,Profit FROM TableName
)
SELECT CAST(SUM(Profit) AS INT) AS Profit FROM CTE
WHERE rn=1
Note:- First you to get the DISTINCT Record then..use sum function...

SQL select specific group from table

I have a table named trades like this:
id trade_date trade_price trade_status seller_name
1 2015-01-02 150 open Alex
2 2015-03-04 500 close John
3 2015-04-02 850 close Otabek
4 2015-05-02 150 close Alex
5 2015-06-02 100 open Otabek
6 2015-07-02 200 open John
I want to sum up trade_price grouped by seller_name when last (by trade_date) trade_status was 'open'. That is:
sum_trade_price seller_name
700 John
950 Otabek
The rows where seller_name is Alex are skipped because the last trade_status was 'close'.
Although I can get desirable output result with the help of nested select
SELECT SUM(t1.trade_price), t1.seller_name
WHERE t1.seller_name NOT IN
(SELECT t2.seller_name FROM trades t2
WHERE t2.seller_name = t1.seller_name AND t2.trade_status = 'close'
ORDER BY t2.trade_date DESC LIMIT 1)
from trades t1
group by t1.seller_name
But it takes more than 1 minute to execute above query (I have approximately 100K rows).
Is there another way to handle it?
I am using PostgreSQL.
I would approach this with window functions:
SELECT SUM(t.trade_price), t.seller_name
FROM (SELECT t.*,
FIRST_VALUE(trade_status) OVER (PARTITION BY seller_name ORDER BY trade_date desc) as last_trade_status
FROM trades t
) t
WHERE last_trade_status <> 'close;
GROUP BY t.seller_name;
This should perform reasonably with an index on seller_name
select
sum(trade_price) as sum_trade_price,
seller_name
from
trades
inner join
(
select distinct on (seller_name) seller_name, trade_status
from trades
order by seller_name, trade_date desc
) s using (seller_name)
where s.trade_status = 'open'
group by seller_name

Firebird Query- Return first row each group

In a firebird database with a table "Sales", I need to select the first sale of all customers. See below a sample that show the table and desired result of query.
---------------------------------------
SALES
---------------------------------------
ID CUSTOMERID DTHRSALE
1 25 01/04/16 09:32
2 30 02/04/16 11:22
3 25 05/04/16 08:10
4 31 07/03/16 10:22
5 22 01/02/16 12:30
6 22 10/01/16 08:45
Result: only first sale, based on sale date.
ID CUSTOMERID DTHRSALE
1 25 01/04/16 09:32
2 30 02/04/16 11:22
4 31 07/03/16 10:22
6 22 10/01/16 08:45
I've already tested following code "Select first row in each GROUP BY group?", but it did not work.
In Firebird 2.5 you can do this with the following query; this is a minor modification of the second part of the accepted answer of the question you linked to tailored to your schema and requirements:
select x.id,
x.customerid,
x.dthrsale
from sales x
join (select customerid,
min(dthrsale) as first_sale
from sales
group by customerid) p on p.customerid = x.customerid
and p.first_sale = x.dthrsale
order by x.id
The order by is not necessary, I just added it to make it give the order as shown in your question.
With Firebird 3 you can use the window function ROW_NUMBER which is also described in the linked answer. The linked answer incorrectly said the first solution would work on Firebird 2.1 and higher. I have now edited it.
Search for the sales with no earlier sales:
SELECT S1.*
FROM SALES S1
LEFT JOIN SALES S2 ON S2.CUSTOMERID = S1.CUSTOMERID AND S2.DTHRSALE < S1.DTHRSALE
WHERE S2.ID IS NULL
Define an index over (customerid, dthrsale) to make it fast.
in Firebird 3 , get first row foreach customer by min sales_date :
SELECT id, customer_id, total, sales_date
FROM (
SELECT id, customer_id, total, sales_date
, row_number() OVER(PARTITION BY customer_id ORDER BY sales_date ASC ) AS rn
FROM SALES
) sub
WHERE rn = 1;
İf you want to get other related columns, This is where your self-answer fails.
select customer_id , min(sales_date)
, id, total --what about other colums
from SALES
group by customer_id
So simple as:
select CUSTOMERID min(DTHRSALE) from SALES group by CUSTOMERID

How to Retrieve Maximum Value of Each Group? - SQL

There is a table tbl_products that contains data as shown below:
Id Name
----------
1 P1
2 P2
3 P3
4 P4
5 P5
6 P6
And another table tbl_inputs that contains data as shown below:
Id Product_Id Price Register_Date
----------------------------------------
1 1 10 2010-01-01
2 1 20 2010-10-11
3 1 30 2011-01-01
4 2 100 2010-01-01
5 2 200 2009-01-01
6 3 500 2011-01-01
7 3 270 2010-10-15
8 4 80 2010-01-01
9 4 50 2010-02-02
10 4 92 2011-01-01
I want to select all products(id, name, price, register_date) with maximum date in each group.
For Example:
Id Name Price Register_Date
----------------------------------------
3 P1 30 2011-01-01
4 P2 100 2010-01-01
6 P3 500 2011-01-01
10 P4 92 2011-01-01
select
id
,name
,code
,price
from tbl_products tp
cross apply (
select top 1 price
from tbl_inputs ti
where ti.product_id = tp.id
order by register_date desc
) tii
Although is not the optimum way you can do it like:
;with gb as (
select
distinct
product_id
,max(register_date) As max_register_date
from tbl_inputs
group by product_id
)
select
id
,product_id
,price
,register_date
from tbl_inputs ti
join gb
on ti.product_id=gb.product_id
and ti.register_date = gb.max_register_date
But as I said earlier .. this is not the way to go in this case.
;with cte as
(
select t1.id, t1.name, t1.code, t2.price, t2.register_date,
row_number() over (partition by product_id order by register_date desc) rn
from tbl_products t1
join tbl_inputs t2
on t1.id = t2.product_id
)
select id, name, code, price, register_date
from cte
where rn = 1
Something like this..
select id, product_id, price, max(register_date)
from tbl_inputs
group by id, product_id, price
you can use the max function and the group by clause. if you only need results from the table tbl_inputs you even don't need a join
select product_id, max(register_date), price
from tbl_inputs
group by product_id, price
if you need field from the tbl_prducts you have to use a join.
select p.name, p. code, i.id, i.price, max(i.register_date)
from tbl_products p join tbl_inputs i on p.id=i.product_id
grooup by p.name, p. code, i.id, i.price
Try this:
SELECT id, product_id, price, register_date
FROM tbl_inputs T1 INNER JOIN
(
SELECT product_id, MAX(register_date) As Max_register_date
FROM tbl_inputs
GROUP BY product_id
) T2 ON(T1.product_id= T2.product_id AND T1.register_date= T2.Max_register_date)
This is, of course, assuming your dates are unique. if they are not, you need to add the DISTINCT Keyword to the outer SELECT statement.
edit
Sorry, I didn't explain it very well. Your dates can be duplicated, it's not a problem as long as they are unique per product id. if you can have duplicated dates per product id, then you will have more then one row per product in the outcome of the select statement I suggested, and you will have to find a way to reduce it to one row per product.
i.e:
If you have records like that (when the last date for a product appears more then once in your table with different prices)
id | product_Id | price | register_date
--------------------------------------------
1 | 1 | 10.00 | 01/01/2000
2 | 1 | 20.00 | 01/01/2000
it will result in having both of these records as outcome.
However, if the register_date is unique per product id, then you will get only one result for each product id.

Find the the value of one field that matches the maximum value of data in another field

I'm trying to write a query that gets the value of one field that's associated with the maximum value of another field (or fields). Let's say I have the following table of data:
OrderID CustomerID OrderDate LocationID
1 4 1/1/2001 1001
2 4 1/2/2001 1003
3 4 1/3/2001 1001
4 5 1/4/2001 1001
5 5 1/5/2001 1001
6 5 1/6/2001 1003
7 5 1/7/2001 1002
8 5 1/8/2001 1003
9 5 1/8/2001 1002
Grouping by CustomerID, I want to get the maximum OrderDate and then the LocationID associated with whatever is the maximum OrderDate. If there are several records that share the maximum order date, then take the LocationID associated with the maximum OrderID from among those records with the maximum date.
The final set of data should look like this:
CustomerID OrderDate LocationID
4 1/3/2001 1001
5 1/8/2001 1002
I had been trying to write a query with lots of nested subqueries and ugly joins, but I'm not really getting anywhere. What SQL do I need to write to help me get this result.
with cte As
(
select *,
row_number() over (partition by CustomerID
order by OrderDate desc, OrderId desc) as rn
from yourtable
)
select CustomerID, OrderDate,LocationID
from cte
where rn=1;
SELECT
C.Name,
C.CustomerID,
X.*
FROM
Customers C
CROSS APPLY (
SELECT TOP 1 OrderDate, LocationID
FROM Orders O
WHERE C.CustomerID = O.CustomerID
ORDER BY OrderDate Desc, OrderID Desc
) X
If you will pull any columns from the Customers table, this will probably outperform other methods.
If not, then the Row_Number answer, pulling only from Orders, will probably be best. But if you restrict by Customer in any way, then the CROSS APPLY will again be best. Possibly by a big margin.
The trick is to use a subquery as a value, not as a join:
select customerId,orderDate,locationId
from orders o1
where orderDate = (
select top 1 orderdate
from orders o2
where o1.customerId = o2.customerId
order by orderdate desc
)