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`
Related
I need to join 2 tables with the 2nd table with JOIN condition that is based on the 3rd JOIN table.
The problem is, I couldn't get the value from 3rd JOIN table while still joining the 2nd table.
View on DB Fiddle
Table A: users
-------
user_id INT PRIMARY
Table B: orders
-------
order_id INT PRIMARY
user_id INT
invoice_id INT
Table C: invoices
-------
invoice_id INT PRIMARY
invoice_status VARCHAR [voided, paid]
This is what I wish to do:
SELECT
A.user_id,
B.order_id,
C.invoice_id,
C.invoice_status
FROM users A
LEFT JOIN orders B
ON (B.user_id = A.user_id
AND C.invoice_status = 'paid')
LEFT JOIN invoices C
ON (C.invoice_id = B.invoice_id)
The 9th line AND C.invoice_status = 'paid') is referring to table C which has not been joined yet, so this query will cause Error in query (1054): Unknown column 'C.invoice_status' in 'on clause'
The reason I cannot have this condition in WHERE clause is because I still want to return all users records regardless of whether they have any order or 'paid' invoice or not. So adding WHERE invoice_status = 'paid' will NOT return users without any order and users with invoice_status = 'voided'.
Also, when a user has two orders linked to two different invoice records, but 1 invoice's invoice_status is 'paid', while the other is 'voided', I only want to return the 'paid' record. There can be many voided invoices, but only 1 'paid' invoice record.
By the way, this is enforced: sql_mode=only_full_group_by, so I can't do GROUP BY user_id without having some kind of aggregated or condition on order_id & invoice_id fields.
SELECT
A.user_id,
B.order_id,
C.invoice_id,
C.invoice_status
FROM users A
LEFT JOIN orders B
ON (B.user_id = A.user_id)
LEFT JOIN invoices C
ON (C.invoice_id = B.invoice_id);
user_id
order_id
invoice_id
invoice_status
1
1
1
voided
1
2
2
paid
2
3
1
voided
3
NULL
NULL
NULL
My desired result:
user_id
order_id
invoice_id
invoice_status
1
2
2
paid
2
3
1
voided
3
NULL
NULL
NULL
Each user_id must only return once, with invoice_status = 'paid' being the preferred row when there are multiple related orders.
Appreciate it if anyone has any idea how to achieve this.
Thanks!
Use ROW_NUMBER() function And number the rows according to user_id and sort according to invoice_status then fetch the first row
SELECT user_id,
order_id,
invoice_id,
invoice_status
FROM
(SELECT
A.user_id,
B.order_id,
C.invoice_id,
C.invoice_status,
ROW_NUMBER() OVER(PARTITION BY A.user_id ORDER BY C.invoice_status) AS num
FROM users A
LEFT JOIN orders B
ON (B.user_id = A.user_id)
LEFT JOIN invoices C
ON (C.invoice_id = B.invoice_id)) t
WHERE num = 1
db<>fiddle
I am able to solve it by using EXISTS thanks to ProGu's comment.
SELECT
A.user_id,
B.order_id,
C.invoice_id,
C.invoice_status
FROM users A
LEFT JOIN orders B
ON B.user_id = A.user_id
AND EXISTS( SELECT
1
FROM
invoices
WHERE
invoices.invoice_id = B.invoice_id
AND invoices.invoice_status = 'paid')
LEFT JOIN invoices C
ON C.invoice_id = B.invoice_id
user_id
order_id
invoice_id
invoice_status
1
2
2
paid
2
NULL
NULL
NULL
3
NULL
NULL
NULL
Thanks!
I am forming a sql query like below which gives me an error that i cannot use m1.id in inner where clause.
SELECT
c.id,
m.member_no,
mc.card_no
FROM
customer AS c
LEFT JOIN
(
SELECT
*
FROM
membership
WHERE
creation_date = (SELECT MAX(creation_date) FROM membership AS m1 WHERE m1.cust_id = 123)
) AS m ON m.cust_id = c.id
LEFT JOIN
(
SELECT
*
FROM
member_card
WHERE
emboss_date = (SELECT MAX(emboss_date) FROM member_card AS mc1 WHERE mc1.membership_id = m.id)
) AS mc ON mc.membership_id = m.id
WHERE
c.id = 123
Table :
Customer, Membership, Member_card.
Customer can have many membership and each membership can have many member_card.
Table Customer
id name address
1 amit abc
2 mohit xyz
3 rahul asdf
4 ganesh pqr
Table membership
id member_no creation_date cust_id
21 123 21-09-1978 1
31 234 21-09-1988 2
41 345 21-09-1998 1
51 456 21-09-1977 2
Table member_card
id card_no membership_id emboss_date
111 12345 21 21-09-1978
222 23456 31 21-09-1977
333 34567 21 21-09-1976
444 456789 41 21-09-1975
cust_id is foriegn Key in membership table
membership_id id foreign key in member_card table
Now, i want the customer details of all customer table with latest member_no(w.r.t creation_date) and card_no(w.r.t emboss_date), Even if a customer does not have membership, the customer details should be there. refer the query above i made
So, there should be one record for one customer, i.e the final result should contain 4 rows with data from all three tables
The joins should go with proper subquery
select
c.id, m.member_no, mc.card_no
from customer c
left join (
select * from membership m
where creation_date = (select max(creation_date)
from membership where cust_id = m.cust_id)
) m on m.cust_id = c.id
left join (select * from member_card mc
where emboss_date = (select max(emboss_date)
from member_card where membership_id = mc.membership_id)
) mc on mc.membership_id = m.id
where c.id = 123
Other version with aggregation instead of correlation
select
c.id, m.member_no, mc.card_no
from customer c left join (
select cust_id, max(creation_date) creation_date
from membership
group by cust_id) m
on m.cust_id = c.id left join (
select membership_id, max(emboss_date) emboss_date
from member_card mc
group by membership_id) mc
on mc.membership_id = m.cust_id
where c.id = 123
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
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
I have two tables:
parent-child 'categories':
id name parent_id
1 Food NULL
2 Pizza 1
3 Pasta 2
'transactions':
id amount category_id
1 100 1
2 50 2
3 25 2
I want to return all the Categories along with two total columns:
total = The sum of the amount for all transactions with this category_id
parentTotal = total + the total of all its child categories
Example (using the tables above):
id name parent_id total parentTotal
1 Food NULL 100 175
2 Pizza 1 0 0
3 Pasta 2 75 0
EDIT:
Code updated (based on code from Nedret Recep below) and works fine...
SELECT
tmp1.id, tmp1.name, tmp1.parent_id, tmp1.total, IFNULL(tmp1.total, 0) + IFNULL(tmp2.s, 0) AS parenttotal
FROM
(SELECT
ca.id, ca.name, ca.parent_id, SUM(tr.amount) as total
FROM
categories ca
LEFT JOIN
transactions tr
ON
tr.category_id = ca.id
GROUP BY
ca.id)
AS tmp1
LEFT JOIN
(SELECT
c.id, c.parent_id as categoryid, SUM(t.amount) AS s
FROM
transactions t
RIGHT JOIN
categories c
ON
t.category_id = c.id
GROUP BY
c.parent_id)
AS tmp2
ON tmp2.categoryid = tmp1.id
order by coalesce(tmp1.parent_id, tmp1.id), tmp1.parent_id
I'd really appreciate some help - thanks!
With one inner join we calculate the totals in category which is standard. Then with another inner join we calculate the sums but this time grouping by parent_id. Then we join the two result tables to have both sums in one row. This query will be slow with large tables so an alternative approach on application level would do better.
SELECT
tmp1.id, tmp1.name, tmp1.parent_id, tmp1.total, tmp1.total + tmp2.s AS parenttotal
FROM
(SELECT
ca.id, ca.name, ca.parent_id, SUM(tr.amount) as total
FROM
transactions tr
INNER JOIN
categories ca
ON
tr.categoru_id = ca.id
GROUP BY
ca.id)AS tmp1
LEFT OUTER JOIN
(
SELECT
c.parent_id as categoryid, SUM(t.amount) AS s
FROM
transactions t
INNER JOIN
categories c
ON
t.category_id = c.i
GROUP
BY c.id ) AS tmp2
ON
tmp2.categoryid = tmp.id