Select only the latest version of the data with SQL join - mysql

I have two tables one containg offer information and the other containg products
something like this:
OFFER PRODUCTS
ID Number Version poID offer_id Product how_many
========================== ========================================
1 123 1 1 1 Apple 1
2 123 2 2 1 Banana 2
3 124 1 3 1 Orange 1
4 2 Apple 1
5 2 Banana 2
6 2 Orange 2
7 2 Kiwi 1
8 3 Apple 2
9 3 Banana 3
I would like a list of how many products that are currently offered.
Since OFFER(id = 2) is an update of (id = 1) only (id = 2) should be counted.
How should I best query this?

First you need to get all the latests offers:
select o.id
from offer o
where version = (select max(version)
from offer o2
where o2.number = o.number);
Based on the above you can then get all the products:
select p.*
from products p
where offer_id in (select o.id
from offer o
where version = (select max(version)
from offer o2
where o2.number = o.number));

If id and version correlate:
select sum(how_many) from products p
join offer on p.offer_id=offer.id
join (
select number, max(version) version from offer group by number
) x
on offer.id=x.id and offer.version = x.version

SELECT *
FROM products
WHERE offer_id = (SELECT MAX(id) FROM offer)
or, if you prefer the join syntax
SELECT p.*
FROM products p
INNER JOIN (SELECT MAX(id) id FROM offer) o ON p.offer_id = o.id
Edit (still not completely sure this is what you want without seeing your desired results)
SELECT p.*
FROM products p
INNER JOIN offer o on p.offer_id = o.id
INNER JOIN
(SELECT number, max(version)
FROM offer
GROUP BY number
) oMax ON o.number = oMax.number AND o.version = oMax.version

Try this:
select [list columns here]
from products p
join (select offernumber, max(id) as ID from offer group by offernumber) a
on a.id = p.offer_id
If you need addtional columns from offer other than the offernumber and the id:
select [list columns here]
from products p
join (select offernumber, max(id) as ID from offer group by offernumber) a
on a.id = p.offer_id
join offer o on o.id = a.id

Related

how to populate the data from table which unmapped column in join

I am having below tables and want to populate the PAYID from the PAY table by looking in to CATEGORY_ID for below use case
if PAYID is mapped with CATEGORY_ID then return the PAYID
if PAYID is not mapped to CATEGORY_ID then look for BILL_ID and populate is by joining the NETWORK_CATEGORY and CATEGORY table.
If PAYID is not mapped to CATEGORY_ID and even BILL_ID is not associated with NETWORK_CATEGORY_ID then return the CATEGORY_ID as 3 (Uncategorized) for PAY.
Below is the my tables,
PAY Table
PAYID BILL_ID CATEGORY_ID
101 1 1
102 2
103 3
BILL Table
BILL_ID NAME NETWORK_CATEGORY_ID
1 ABC 42
2 XYZ
3 DSC 23
NETWORK_CATEGORY Table
NETWORK_CATEGORY_ID NAME CATEGORY_ID
42 Electric/gas 1
23 ISP 2
CATEGORIES
CATEGORY_ID NAME
1 Utilities
2 Telecom
3 Uncategorized
And i have written below queries ,
select payid(
select payid from PAY p
where p.CATEGORY_ID in (:categoryIds)
UNION
select payid from PAY p
join BILLS b on p.BILL_ID = b.BILL_ID
where NETWORK_CATEGORY_ID in
(
select NETWORK_CATEGORY_ID
from NETWORK_CATEGORY nc
join CATEGORIES c on nc.CATEGORY_ID = c.CATEGORY_ID
where c.CATEGORY_ID in (:categoryIds)
)
and CATEGORY_ID is null
)
My expectation is that if i passed the categoryIds as 3 or (1,2,3 )then it should return the PAYID as 102 and 101,102,103 respectively.
How can i do that.
Or i can say that how can achieve this task,
If PAYID is not mapped to CATEGORY_ID and even BILL_ID is not associated with NETWORK_CATEGORY_ID then return the CATEGORY_ID as 3 (Uncategorized) for PAY.
By using three union i can achieve that but that not looks an efficient solution.
SELECT p.PAYID
FROM PAY p
WHERE p.CATEGORY_ID IN (2,3)
UNION ALL
SELECT p.PAYID
FROM PAY p
JOIN BILL b
ON p.CATEGORY_ID IS NULL AND p.BILL_ID = b.BILL_ID
JOIN NETWORK_CATEGORY nc
ON b.NETWORK_CATEGORY_ID = nc.NETWORK_CATEGORY_ID
WHERE nc.CATEGORY_ID IN (2,3)
UNION ALL
SELECT p.PAYID
FROM PAY p
LEFT JOIN BILL b
ON p.CATEGORY_ID IS NULL AND p.BILL_ID = b.BILL_ID
LEFT JOIN NETWORK_CATEGORY nc
ON b.NETWORK_CATEGORY_ID = nc.NETWORK_CATEGORY_ID
WHERE (3 IN (2,3) AND p.CATEGORY_ID IS NULL AND nc.CATEGORY_ID IS NULL)
Depending on the size of the dataset and the other criteria being applied to the PAY table, this may perform OK -
SELECT p.PAYID
FROM PAY p
LEFT JOIN BILL b
ON p.CATEGORY_ID IS NULL AND p.BILL_ID = b.BILL_ID
LEFT JOIN NETWORK_CATEGORY nc
ON b.NETWORK_CATEGORY_ID = nc.NETWORK_CATEGORY_ID
WHERE p.CATEGORY_ID IN (2,3)
OR nc.CATEGORY_ID IN (2,3)
OR (3 IN (2,3) AND p.CATEGORY_ID IS NULL AND nc.CATEGORY_ID IS NULL)
Messy but 1) first subquery (x) to cater for no entries, 2) first part of union to check category id, 3) UNION to deal with situation where bill_id and category_id are the same, 4) second part of union to find pay via bill, 5) coalesce to cater for no entries.
select coalesce(payid,3) payid,s.`bill/category id`
from
(select 2 `bill/category id` ) x
left join
(select payid ,category_id as `bill/category id` from pay where category_id = 2
union
select payid ,bill.bill_id
from bill
join network_category nc on bill.network_category_id = nc.network_category_id
join pay on pay.bill_id = nc.category_id
where bill.bill_id = 2
and not exists (select payid from pay where category_id = 2)
) s on s.`bill/category id` = x.`bill/category id`

MySQL limit on child

I saw some articles but I can't implement it on my code,
I Have 3 tables
Category
ID
name
1
winter
2
summer
Product
ID
name
1
bikini
2
scarf
Category_product
id_category
id_product
1
1
2
2
Basically like that, with much more products and more records on category_product as well, so what I want is to select 10 categories with their products but limited by 10 as well...
So I have something like
---Category 1
Product 1
Product 2
Product 3
---Category 2
Product 1
Product 2
Product 3
10 category max and 10 products max
I tried this but only get one product
SELECT c.* FROM category c
INNER JOIN category_product cp ON cp.id_category = c.id
INNER JOIN
(SELECT id_category, MAX(id_product) test
FROM category_product
GROUP BY id_category) cp2 ON cp.id_category = cp2.id_category
AND cp.id_product = cp2.test
so what I want is to select 10 categories with their products but limited by 10 as well
You can use row_number() and limit:
select c.*, cp.product_id
from (select c.*
from categories c
limit 10 -- you might want order by to get particular categories
) c join
(select cp.*,
row_number() over (partition by cp.category_id order by cp.product_id) as seqnum
from category_product cp
) cp
on c.category_id = c.id
where seqnum <= 10;
You can join in products if you want additional information about the products.

Select count of total products as well as out of stock products from table

I have to 3 tables: product, product_to_store, store
product table
id quantity status
1 1 1
2 0 1
3 0 1
4 23 1
product_to_store table
store_id product_id
1 1
2 2
1 3
2 4
store table
id name
1 store1
2 store2
To find total products I can run query to fetch all products in table product where status of product is enabled.
select count(*) from product where status=1
total name
2 Store 1
2 store 2
To find total out of stock products I can run below query after joining all 3 tables and using group by store_id:
Select count(*) as outofproducts from product where quantity=0;
Result come like this:
outofproducts name
1 Store 1
1 store 2
But I want combination of above 2 results in single query like below:
outofproducts total name
1 2 Store 1
1 2 store 2
You'd use conditional aggregatiopn, i.e. sum/count over conditions:
select
s.name,
sum(p.quantity > 0) as in_stock,
sum(p.quantity = 0) as out_of_stock,
count(*) as total
from store s
join product_to_store ps on ps.store_id = s.id
join product p on p.id = ps.product_id
group by s.name
order by s.name;
This makes use of MySQL's true = 1, false = 0. If you don't like it, replace sum(p.quantity = 0) with sum(case when p.quantity = 0 then 1 else 0 end) or count(case when p.quantity = 0 then 1 end).
You can start query from store table so that we will get total rows as store table data.
Then use nested query for each store to get out of product and total product count
select
(Select count(*) as outofproducts from product_to_store ps inner join product p on p.id = ps.product_id where quantity=0 and ps.store_id = s.id ) as outofproducts ,
(Select count(*) as count from product_to_store ps inner join product p on p.id = ps.product_id where ps.store_id = s.id ) as totalCount,
s.name
from store s
You could join the related subquery for count
select t1.name, t1.outofproducts, t2.total
from(
select b.id, b.name , count(*) outofproducts
from product_to_store c
inner join product a on a.id = c.product_id
inner join store b on a.id = c.store_id
where a.quantity = 0
group by b.id, b.name
) t1
inner join (
select b.id, b.name , count(*) total
from product_to_store c
inner join product a on a.id = c.product_id
inner join store b on a.id = c.store_id
group by b.id, b.name
) t2 on t1.id = t2.id

SQL query without foreign key in other table

i have problem with MySQL query, I have two tables - offers and bids:
offers bids
id status id offer_id user_id
1 new 1 1 1
2 new 2 1 2
3 rejected 3 2 1
4 accepted 4 4 4
5 new 5 5 2
6 new 6 6 3
7 new 7 7 1
Assuming that user_id = 2, I need all offers (from table offers)for this user_id which have status ="new", but without these offers which that user has bid (without offer 1 and offer 5)
I tried with "not exists" but without much success. Like:
SELECT distinct o.*
FROM offers o
LEFT JOIN bids AS b
ON o.id = b.offer_id
WHERE o.status = "new" AND (b.user_id!=2
OR NOT EXISTS (SELECT * FROM offers off WHERE b.offer_id = off.id))
And similiar ( i can get result when there is no other users that bid same offer),
but I cant get what I need.
EDIT:
As output in this case I need offers with IDs:
2 6 7 (offers 3 and 4 are not "new" and for offers 1 and 5 that user has already bids)
You don't need a join here. Its very simple
Select offers where status is new
Exclude those offers where user = 2 has bid
Query:
SELECT * FROM offers o
WHERE o.`status` = 'new' AND
o.id NOT IN (SELECT offer_id from bids b WHERE b.user_id = 2)
This should work:
SELECT distinct o.*
FROM offers o
WHERE o.status = "new" AND
o.id not in (select offer_id from bids where user_id = 2);
If you want to display for all the users then try following query:
SELECT distinct o.*
FROM offers o
Inner JOIN bids AS b
ON o.id = b.offer_id
WHERE o.status = "new" AND
Group by b.user_id
having o.id not in (select offer_id from bids b1 where b1.user_id = b.user_id);
SELECT distinct o.*
FROM offers o
JOIN bids b
ON o.id = b.offer_id
WHERE o.status = "new"
AND (o.id not in (select distinct(offer_id) from bids bi where bi.user_id=2 ))

mysql group by and take specific record from right join table

I have database with following data structure with sample data. Each company have multiple members. The relationship is in the company_member table. Please note only required fields I have given below.
company
id title
1 company-1
2 company-2
company_member
companyid memberid
1 1
1 2
1 3
2 4
2 5
2 6
member
id firstname member_type_id
1 Name-1 2
2 Name-2 3
3 Name-3 3
4 Name-4 3
5 Name-5 2
6 Name-6 1
member_type
id user_level
1 0
2 1
3 2
I want list of unique companies with one member from each. But the member should be the lowest user_level within the company. i.e, following result should come;
result
companyid company_title memberid member_name user_level
1 company-1 1 Name-1 1
2 company-2 6 Name-6 0
I want to know how to get one member with lowest user level among the same company.
This is a bit complicated one, however this is one way of doing it using not exists, for bigger tables its wise to use not exits since using pivot tables it will not use index.
select
c.id,
c.title,
m.id as member_id,
m.firstname,
mt.user_level
from company_member_map cmp
join company c on c.id = cmp.companyid
join member m on m.id = cmp.memberid
join member_type mt on mt.id = m.member_type_id
where not exists
(
select 1 from company_member_map t1
join member t2 on t2.id = t1.memberid
join member_type t3 on t3.id = t2.member_type_id
where
t1.companyid = cmp.companyid
and t3.user_level < mt.user_level
)
DEMO
Finally I found solution:
select c.id companyid, c.title company_title, m.firstname member_name, mt.user_level
from company c
inner join company_member cm on cm.companyid = c.id
inner join member m on m.id = cm.memberid
inner join member_type mt on mt.id = m.member_type_id
inner join
(select c1.id companyid, mt1.user_level
from company c1
join company_member cm1 on cm1.companyid = c1.id
join member m1 on m1.id = cm1.memberid
join member_type mt1 on mt1.id = m1.member_type_id
group by c1.id,m1.id
order by user_level asc
) sq on c.id = sq.companyid and sq.user_level = mt.user_level
group by c.id;
Correct this, if anyone have better solution or simplified solution.
Check this SQL Fiddle