Selecting values in columns based on other columns - sql

I have two tables, info and transactions.
info looks like this:
customer ID Postcode
1 ABC 123
2 DEF 456
and transactions looks like this:
customer ID day frequency
1 1/1/12 3
1 3/5/12 4
2 4/6/12 2
3 9/9/12 1
I want to know which day has the highest frequency for each postcode.
I know how to reference from two different tables but im not too sure how to reference multiple columns based on their values to other columns.
The output should be something like this:
customer ID postcode day frequency
1 ABC 123 3/5/12 4
2 DEF 456 4/6/12 2
3 GHI 789 9/9/12 1
and so on.

You can filter with a correlated subquery:
select
i.*,
t.day,
t.frequency
from info i
inner join transactions t on t.customerID = i.customerID
where t.frequency = (
select max(t.frequency)
from info i1
inner join transactions t1 on t1.customerID = i1.customerID
where i1.postcode = i.postcode
)
Or, if your RBDMS supports window functions, you can use rank():
select *
from (
select
i.*,
t.day,
t.frequency,
rank() over(partition by i.postcode order by t.frequency desc)
from info i
inner join transactions t on t.customerID = i.customerID
) t
where rn = 1

Related

SQL help i need to find the inventory remaining in my office

In sql help i have 3 tables, table one is asset table which is as follow
id
asset_code
asset_name
asset_group
asset_quantity
1
A001
demo asset
4
5
2
A002
demo asset 2
6
3
and another table is asset_allocation
id
asset_id
allocated_quantity
allocated_location
1
1
2
IT office
2
1
1
main hall
the last table is asset_liquidated which will present assets that are no longer going to be used
id
asset_id
liquidated_quantity
1
1
2
2
1
1
lets say i have 5 computers and i have allocated 3 computers and 1 is no longer going to be used so i should be remaining with 1 computer so now how do i make sql auto generate this math for me
You need to use aggregation and the join your tables -
SELECT id, asset_code, asset_name, asset_group, asset_quantity,
asset_quantity - COALESCE(AA.allocated_quantity, 0) - COALESCE(AL.liquidated_quantity, 0) available_quantity
FROM asset A
LEFT JOIN (SELECT asset_id, SUM(allocated_quantity) allocated_quantity
FROM asset_allocation
GROUP BY asset_id) AA ON A.id = AA.asset_id
LEFT JOIN (SELECT asset_id, SUM(liquidated_quantity) liquidated_quantity
FROM asset_liquidated
GROUP BY asset_id) AL ON A.id = AL.asset_id
This query will give you -1 as available_quantity for asset_id 1 as you have only 5 available, 3 of them are allotted and 3 are liquidated as per your sample data.
Please see if this helps
SELECT
asset_quantity AS Total_Assets
,ISNULL(allocated_quantity, 0) allocated_quantity
,ISNULL(liquidated_quantity, 0) liquidated_quantity
FROM asset
LEFT OUTER JOIN (
SELECT
asset_id, SUM(allocated_quantity) AS allocated_quantity
FROM asset_allocation
GROUP BY asset_id
) asset_allocation2
ON asset_allocation2.asset_id = asset.id
LEFT OUTER JOIN (
SELECT
asset_id, SUM(liquidated_quantity) AS liquidated_quantity
FROM asset_liquidated
GROUP BY asset_id
) asset_liquidated 2
ON asset_liquidated 2.asset_id = asset.id

SQL: City wise,product wise latest order ID

I have tables:
tbl-city
id city
---------
1 A
2 B
3 C
tbl-orders
ord_id product_id city date
----------------------------------
1 1 1 12/3/18
2 1 2 13/3/18
3 2 3 12/4/18
4 1 3 14/4/18
5 3 2 11/2/18
6 1 1 15/1/18
7 2 3 15/4/28
I need to get all latest order Id from table orders
by city wise and product wise using date
Like this:
ord_id product_id city
---------------------------
1 1 1
2 1 2
7 2 3
5 3 2
How can I get this?
JOIN/INNER JOIN or any other way?
You can use correlated subquery :
select o.*
from orders o
where date = (select max(o1.date)
from orders o1
where o1.product_id = o.product_id and o1.city = o.city
);
If ord_id increases along with the date (as would be typical), then a simple aggregation works.
select product_id, city, max(ord_id)
from tbl_ord
group by product_id, city;
Otherwise, the typical method would use row_number():
select o.*
from (select o.*,
row_number() over (partition by product_id, city order by date desc) as seqnum
from tbl_ord o
) o
where seqnum = 1;
In older versions of MySQL, a correlated subquery is a good route. But if you want exactly one row per product and city, use ord_id:
select o.*
from tbl_ord o
where o.ord_id = (select o2.ord_id
from tbl_ord o2
where o2.product_id = o.product_id and o2.city = o.city
order by o2.date desc
limit 1
);

how to get value using latest date from one table and joining to another table

i have 1 table inventory_movement here is data in table
product_id | staff_name | status | sum | reference_number
--------------------------------------------------
1 zes cp 1 000122
2 shan cp 4 000133
i have another table inventory_orderproduct where i have cost date
orderdate product_id cost
--------------------------------
01/11/2018 1 3200
01/11/2018 2 100
02/11/2018 1 4000
02/11/2018 1 500
03/11/2018 2 2000
i want this result
product_id| staff_name | status | sum reference_number | cost
--------------------------------------------------------------
1 zes cp 1 000122 4000
2 shan cp 4 000133 2000
here is my query
select ipm.product_id,
case when ipm.order_by_id is not null then
(select au.first_name from users_staffuser us inner join auth_user au on us.user_id= au.id
where us.id = ipm.order_by_id) else '0' end as "Staff_name"
,ipm.status,
Sum(ipm.quantity), ip.reference_number
from inventory_productmovement ipm
inner join inventory_product ip on ipm.product_id = ip.id
inner join users_staffuser us on ip.branch_id = us.branch_id
inner join auth_user au on us.user_id = au.id
AND ipm.status = 'CP'
group by ipm.product_id, au.first_name, ipm.status,
ip.reference_number, ip.product_name
order by 1
Here is the solution of your question.its working fine.if you like the answer please vote!
SELECT i.product_id,i.staff_name,i.status,i.sum reference_number ,s.Cost
FROM (SELECT product_id,MAX(cost) AS Cost
FROM inventory_orderproduct
GROUP BY product_id ) s
JOIN inventory_movement i ON i.product_id =s.product_id
In the given situation, this should work fine:
Select table1.product_id, table2.staff_name, table2.status, table2.reference_number,
MAX(table1.cost)
FROM table2
LEFT JOIN table1 ON table1.product_id = table2.product_id
GROUP BY table2.product_id, table2.staff_name, table2.status, table2.reference_number
You can use the below query to get MAX cost for products
SELECT i.product_id,i.staff_name,i.status,i.sum reference_number ,s.MAXCost
FROM (SELECT product_id,MAX(cost) AS MAXCost
FROM inventory_orderproduct
GROUP BY product_id ) s
JOIN inventory_movement i ON i.product_id =s.product_id
For Retrieving the cost using the latest date use the below query
WITH cte as (
SELECT product_id,cost
,ROW_NUMBER() OVER (PARTITION BY product_id ORDER BY orderdate DESC) AS Rno
FROM inventory_orderproduct )
SELECT i.product_id,i.staff_name,i.status,i.sum reference_number ,s.Cost
FROM cte s
JOIN inventory_movement i ON i.product_id =s.product_id
WHERE s.Rno=1
You can use below query it will pick the data according to the latest date
WITH result as (
SELECT product_id,cost
,ROW_NUMBER() OVER (PARTITION BY product_id ORDER BY date DESC)
FROM inventory_orderproduct )
SELECT i.product_id,i.staff_name,i.status,i.sum reference_number ,s.Cost
FROM result s
JOIN inventory_movement i ON i.product_id =s.product_id

find all rows after the recent update using oracle

I tried below query to bring all rows after last Action="UNLOCKED", but ORDER BY is not allowed in subquery it seems.
SELECT *
FROM TABLE
WHERE id >= (SELECT MAX(id)
FROM TABLE
WHERE ACTION='UNLOCKED' AND action_id=123
ORDER BY CREATE_DATE DESC);
Sample data
Id action_id Action ... CREATE_DATE
1 123 ADD 03/18/2018
2 123 Unlocked 03/19/2018
3 123 Updated1 03/19/2018
4 123 Updated2 03/19/2018
5 123 Unlocked 03/20/2018
6 123 Updated3 03/20/2018
7 123 Updated4 03/20/2018
Output should be rows with id 5,6,7. What should i use to get this output
you could use an inner join on subselect for max create_date
select * from TABLE
INNER JOIN (
select max(CREATE_DATE) max_date
from TABLE
where Action = 'Unlocked' ) T on t.max_date = TABLE.CREATE_DATE
You need not order the inner query because it will return only one value. You can do it as follows
SELECT * FROM TABLE WHERE id >= (select max(id) from TABLE where ACTION='UNLOCKED' and action_id=123);

Join a dynamic number of rows in postgres

Let's say I have the following tables:
Batch Items
---+----- ---+----------+--------
id | size id | batch_id | quality
---+----- ---+----------+--------
1 | 10 1 | 1 | 9
2 | 2 2 | 1 | 10
3 | 2 | 1
4 | 2 | 2
5 | 2 | 1
6 | 2 | 9
I have batches of items. They are sent by batches of size batch.size. An item is broken if it's quality is <= 3.
I want to know the number of broken items in the last batches sent:
batch_id | broken_item_count
---------+---------------------
1 | 0
2 | 2 (and not 3)
My idea is the following:
SELECT batch.id as batch_id, COUNT(broken_items.*) as broken_item_count
FROM batch
INNER JOIN (
SELECT id
FROM items
WHERE items.quality <= 3
ORDER BY items.id asc
LIMIT batch.size -- invalid reference to FROM-clause entry for table "batch"
) broken_items ON broken_items.batch_id = batch.id
(I would ORDER BY items.shipped_at. But for simplicity, I order by items.id)
But this query shows me the error I put as the comment.
How can I limit the number of joined items based on the batch.size that is different for each row ?
Is there any other way to achieve what I want ?
SELECT b.id AS batch_id
, count(i.quality < 4 OR NULL) AS broken_item_count
FROM batch b
LEFT JOIN (
SELECT batch_id, quality
, row_number() OVER (PARTITION BY batch_id ORDER BY id DESC) AS rn
FROM items
) i ON i.batch_id = b.id
AND i.rn <= b.size
GROUP BY 1
ORDER BY 1;
SQL Fiddle with added examples.
This is much like #Clodoaldos's answer, but with a couple of differences. Most importantly:
You want to count the broken items in the last batches sent, so we have to ORDER BY id DESC
If there can be batches without items at all you need to use LEFT JOIN instead of a plain JOIN or those batches are excluded.
Consequently, the check i.rn <= b.size needs to move from the WHERE clause to the JOIN clause.
SQL Fiddle
select
b.id as batch_id,
count(quality <= 3 or null) as broken_item_count
from
batch b
inner join (
select
id, quality, batch_id,
row_number() over (partition by batch_id order by id) as rn
from items
) i on i.batch_id = b.id
where rn <= b.size
group by b.id
order by b.id
From what I understand the count of defective items cannot be greater than the batch size.
EDIT: After reading your comments, I think using the RANK() function, and then join by rank and size should work for you. The following query attempts that.
SELECT b.id,
SUM(CASE WHEN i1.quality <= 3 THEN 1 ELSE 0END) as broken_item_count
FROM BATCH as b
LEFT JOIN (SELECT i.id, i.batch_id, i.quality,
RANK() OVER(PARTITION BY i.batch_id ORDER BY i.id) as RANK
FROM ITEMS as i) as i1 ON b.id = i1.batch_id AND i1.RANK <= b.size
GROUP BY b.id
EDIT2: Updated the query with a LEFT JOIN to cover the case where there are no samples in some batch.