SQL select a row by highest value from a group - sql

I'm trying to select the highest value(calculated) of a group with distinct selection of other columns
according to the table-data below, i want to select the rows, which have the highest amount (Qty-Plan) and a distinct selection of Len and Wid
Table data is as follow
+-----------+-----------+---------+---------+------------+---------+
| Ident | Name | Len | Wid | Qty | Plan |
+-----------+-----------+---------+---------+------------+---------+
| 12345 | Name1 | 1500 | 1000 | 20 | 5 |
| 23456 | Name1 | 1500 | 1000 | 30 | 13 |
| 34567 | Name1 | 2500 | 1000 | 10 | 2 |
| 45678 | Name1 | 2500 | 1000 | 10 | 4 |
| 56789 | Name1 | 1500 | 1200 | 20 | 3 |
| 00001 | Name2 | 1500 | 1200 | 10 | 6 |
| 00002 | Name2 | 1500 | 1200 | 20 | 7 |
| 00003 | Name3 | 1500 | 1200 | 30 | 5 |
| 00004 | Name3 | 1500 | 1200 | 40 | 4 |
+-----------+-----------+---------+---------+------------+---------+
with my query i cant erase the "lower" values:
select a.Ident ,a.Name, a.Len,a.Wid, a.Qnt-a.Plan as Amount
from table a
join (select ident, max(Qnt - Plan) Amount
from table
where Name = 'Name1'
group by Ident, Len, Wid) b
on b.Ident = a.Ident and b.Amount = a.Qnt-a.Plan
order by Amount desc
off-topic question: why i cant use -> where b.Amount = a.Amount (he does not know a.Amount ) ???
my desired select should look like:
+-----------+-----------+---------+---------+------------+
| Ident | Name | Len | Wid | Amount |
+-----------+-----------+---------+---------+------------+
| 56789 | Name1 | 1500 | 1200 | 18 |
| 23456 | Name1 | 1500 | 1000 | 17 |
| 34567 | Name1 | 2500 | 1000 | 8 |
+-----------+-----------+---------+---------+------------+
thanks a lot in advance

It's not clear what kind of database you're using, but this solution should work on any DB:
SELECT tab.Ident,
tab.Name,
tab.Len,
tab.Wid,
(tab.Qty - tab.Plan) AS Amount
FROM (SELECT Name,
Len,
Wid,
MAX(Qty-Plan) AS Amount
FROM my_table
GROUP BY Name,
Len,
Wid
) AS grouped
JOIN my_table tab
ON grouped.Name = tab.Name
AND grouped.Len = tab.Len
AND grouped.Wid = tab.Wid
AND grouped.Amount = (tab.Qty - tab.Plan)
AND tab.Name = 'Name1'

Another approach, using window functions to simplify things:
SELECT ident, name, len, wid, qnt - [plan] AS amount
FROM (SELECT *, row_number() OVER (PARTITION BY len, wid ORDER BY qnt - [plan] DESC) AS rn
FROM test WHERE name = 'Name1') AS sq
WHERE rn = 1
ORDER BY amount DESC;
SQL Fiddle example.

Related

SQL query for the store with the minimum price of an item in the city

I have this table
(city,storeID,itemID,price)
I need to return for each city,itemID the storeID with the minimum price for the item and the minimum price itself ( city,itemID,storeIDmin,minprice).
Can someone help me with this query ?
Thanks!
I solved this with Join and Subquery (Also possible to use "WITH AS" Clause if you work on oracle DB):
SELECT table1.city, table1.itemID, table1.storeID as storeIDmin, subquery.min_price
FROM table1
JOIN (select city, itemID, min(price) as min_price from table1
group by city,itemID) AS subquery
ON table1.city = subquery.city
AND table1.itemID = subqueryitemID
AND table1.price =
subquery.min_price
the result for example:
+------+---------+--------+-------+
| city | storeID | itemID | price |
+------+---------+--------+-------+
| 1 | 1 | 1 | 70 |
| 1 | 2 | 1 | 60 |
| 2 | 1 | 1 | 100 |
| 2 | 1 | 2 | 90 |
| 2 | 2 | 1 | 88 |
| 3 | 1 | 1 | 70 |
+------+---------+--------+-------+
will result:
+------+--------+----------+-------+
| city | itemID | storeMin | price |
+------+--------+----------+-------+
| 2 | 1 | 1 | 88 |
| 3 | 1 | 1 | 70 |
| 2 | 2 | 1 | 90 |
| 1 | 1 | 2 | 60 |
+------+--------+----------+-------+
You can approach this with a correlated subquery:
select t.*
from t
where t.price = (select min(t2.price) from t t2 where t2.itemId = t.itemId);

I want to add sum for running value on a column but if sequence fails then we have don't have to add

I have table like this
+----+--------+------+------+
| id | state | num | pop |
+----+--------+------+------+
| 1 | ny | 1 | 100 |
| 1 | ny | 2 | 200 |
| 1 | ny | 3 | 600 |
| 1 | ny | 6 | 400 |
| 1 | ny | 7 | 300 |
| 1 | ny | 14 | 1000 |
| 2 | nj | 3 | 250 |
+----+--------+------+------+
I want output as below
+---+----+----+------+------+
| 1 | ny | 1 | 100 | 900 |
| 1 | ny | 2 | 200 | 900 |
| 1 | ny | 3 | 600 | 900 |
| 1 | ny | 6 | 400 | 700 |
| 1 | ny | 7 | 300 | 700 |
| 1 | ny | 14 | 1000 | 1000 |
| 2 | nj | 3 | 250 | 250 |
+---+----+----+------+------+
So if there is a seq in num column then we have to add the pop column . So first 3 columns num column has 1,2,3 which is in sequence so we are adding pop column 100+200+600 and displaying as new column.
I tried below code but I am not receiving desired out put
select id, state,num, pop,
sum(pop) over (partition by id, state order by num )
from table
If you subtract a sequence, the values will be constant for the values in a row. Then you can use window functions:
select t.*,
sum(pop) over (partition by state, num - seqnum) as new_population
from (select t.*,
row_number() over (partition by state order by num) as seqnum
from t
) t;
Here is a db<>fiddle (using Postgres).

Aggregation in Join and where

I have this Query for Invertory Balance and work well:
Select A.BATCH_ID ,
A.QTY_MOV - IsNull(B.QTY_USED,0) As BALANCE
From P_BATCH_PRODUC A
Left OUTER Join (Select MATERIAL_ID,
BATCH_MATERIAL_ID),
SUM(QTY_INS) QTY_USED
From CONSUMPTION
Group By MATERIAL_ID, BATCH_MATERIAL_ID) As B
On B.MATERIAL_ID= A.PRODUCT_ID
And A.BATCH_ID = B.BATCH_MATERIAL_ID"
Where A.QTY_MOV - IsNull(B.QTY_USED,0) > 0
AND A.PRODUCT_ID= 1
and A.BATCH_ID = 1
But now, it's possible to have more than one A.QTY_MOV for each A.BATCH_ID , so i need to Change A.QTY_MOV to Sum(A.QTY_MOV ). What do I need to change for that?
Sample:
Table A
+------------+------------+---------+
| Product_ID | Batch_ID | Qty_Mov |
+------------+------------+---------+
| 1 | 1 | 100 |
| 1 | 1 | 150 |
| 2 | 1 | 80 |
| 1 | 3 | 100 |
| 1 | 4 | 100 |
+------------+------------+---------+
Table B
+------------------+------------+------------+----------+--+
| BATCH_MATERIAL_ID| Product_ID | Batch_ID | Qty_USED | |
+------------------+------------+------------+----------+--+
| 1 | 1 | 1 | 80 | |
| 2 | 1 | 1 | 10 | |
| 3 | 1 | 2 | 150 | |
| 4 | 1 | 3 | 80 | |
+------------------+------------+------------+----------+--+
This is what I want
Batch_ID BALANCE
---------- ---------------
1 160
Based strictly on the question, it sounds like you want a window function:
Select A.BATCH_ID ,
SUM(A.QTY_MOV) OVER (PARTITION BY A.BATCH_ID) - IsNull(B.QTY_USED,0) As BALANCE
I don't know if this does anything useful. If it does not, you should ask a new question with sample data and an explanation of logic.

Return the row with the value of the previous row within the same group (Oracle Sql)

I have a tabel that looks like this:
|--------+------+---------|------|
| Head | ID | Amount | Rank |
|--------+------+---------|------|
| 1 | 10 | 1000 | 1 |
| 1 | 11 | 1200 | 2 |
| 1 | 12 | 1500 | 3 |
| 2 | 20 | 3400 | 1 |
| 2 | 21 | 3600 | 2 |
| 2 | 22 | 4200 | 3 |
| 2 | 23 | 1700 | 4 |
|--------+------+---------|------|
I want a new column (New_column) that does the following:
|--------+------+---------|------|------------|
| Head | ID | Amount | Rank | New_column |
|--------+------+---------|------|------------|
| 1 | 10 | 1000 | 1 | 1000 |
| 1 | 11 | 1200 | 2 | 1000 |
| 1 | 12 | 1500 | 3 | 1200 |
| 2 | 20 | 3400 | 1 | 3400 |
| 2 | 21 | 3600 | 2 | 3400 |
| 2 | 22 | 4200 | 3 | 3600 |
| 2 | 23 | 1700 | 4 | 4200 |
|--------+------+---------|------|------------|
Within each Head number, if rank is not 1, takes the amount of row within the Head number with Rank number before it (Rank 2 takes the amount of Rank 1 within the same Head and Rank 3 takes the amount of Rank 2 within the same Head and so on...)
I know how to fix it with a For loop in other programming languages but Don't know how to do it with SQL.
I think you basically want lag():
select t.*,
lag(amount, 1, amount) over (partition by head order by rank) as new_column
from t;
The three-argument form of lag() allows you to provide a default value.
You can join the same table(subquery) on rank-1 of derived table.
select t1.*,case when t1.rank=1 then amount else t2.amount new_amount
from your_table t1 left join (select Head,ID,Amount,Rank from your_table) t2
on t1.head=t2.head and t1.rank=t2.rank-1
You can use this update:
UPDATE your_table b
SET New_column = CASE WHEN rank = 1 then Amount
ELSE (select a.Amount FROM your_table a where a.ID = b.ID and a.rank = b.rank-1) END

Generate report using SUM

I've got two SQL Server 2005 tables: MainTable and Orders.
MainTable:
| MainID | Serial |
-------------------
| 1 | A00001 |
| 2 | B00002 |
Orders:
| OrderID | MainID | Name | Value |
-----------------------------------
| 1 | 2 | John | 100 |
| 2 | 2 | Mike | 200 |
| 3 | 1 | John | 150 |
| 4 | 1 | Mike | 350 |
| 5 | 1 | John | 200 |
| 6 | 2 | John | 500 |
| 7 | 1 | Mike | 50 |
I want to get something like this:
|Serial | Name | Total |
-----------------------
| A00001 | John | 350 |
| A00002 | John | 600 |
| A00001 | Mike | 400 |
| A00002 | Mike | 200 |
SELECT
m.serial,
o.name,
SUM(o.value)
FROM
main m
INNER JOIN order o ON m.mainid = o.mainid
GROUP BY
o.name,
m.serial
select serial, name, sum(value) as total
from maintable m inner join orders o on
m.mainID = o.mainID
group by
serial, name
SELECT
M.SERIAL, O.NAME, SUM(VALUE) AS TOTAL
FROM MAINTABLE M JOIN ORDERS O ON O.MAINID=M.MAINID
GROUP BY M.SERIAL, O.NAME
select Serial , name , Total
from MainTable m,
( select MainID ,name, SUM(value) Total from Orders o group by MainID , Name ) O
where m.MainID= O.MainID