Inner join and left join not working as expected in mysql - mysql

I have 3 tables
table_supplier_bills - bill_id, supplier_id, date
table_supplier_bill_details - bill_id, product_id, quantity, rate
table_supplier_bill_payment_details - id, bill_id, payment_date, amount
I want to get all the bills with their bill_amount and paid_amount.
This is my query.
select
SB.bill_id,
SB.date, SB.supplier_id,
SUM(SBD.quantity * SBD.rate) as bill_amount,
COALESCE(SUM(SBPD.payment_amount), 0.00) as paid_amount
from table_supplier_bills SB
INNER JOIN
table_supplier_bill_details SBD
ON SB.bill_id = SBD.bill_id
LEFT JOIN table_supplier_bill_payment_details SBPD
ON SBD.bill_id = SBPD.bill_id
group by SBD.bill_id;
But this query doesn't give correct paid_amount if there are multiple rows in table_supplier_bill_details for a bill. in case of multiple rows query gives the paid_amount multiplied by as many rows are in table_supplier_bill_details for that table.
Can anyone help me what is wrong here?

Use a correlated query instead :
SELECT SB.bill_id,
SB.date,
SB.supplier_id,
SUM(SBD.quantity * SBD.rate) as bill_amount,
COALESCE((SELECT SUM(SBPD.payment_amount)
FROM table_supplier_bill_payment_details SBPD
WHERE SBD.bill_id = SBPD.bill_id ),0.00) as paid_amount
FROM table_supplier_bills SB
INNER JOIN table_supplier_bill_details SBD
ON SB.bill_id = SBD.bill_id
GROUP BY SBD.bill_id;

Related

Is it possible that each condition in "case when" has a different table?

I have a query like below
select
COALESCE(Clinic,'total') as Clinic,
sum(Non_Billable) as Non_Billable,
sum(initial_Non_Billable) as initial_Non_Billable,
sum(Non_Billable)/NULLIF(sum(initial_Non_Billable),0) as Non_Billable_initial_revenue
FROM (
select
businesses.label as Clinic,
sum(CASE
WHEN appointment_types.category IN (
'Others',
'Non-Billable')
and appointment_types.name like '%initial%'
then invoices.net_amount ELSE 0 END)
as Non_Billable,
count(CASE
WHEN appointment_types.category IN (
'Others',
'Non-Billable')
and appointment_types.name like '%initial%'
then appointment_types.name ELSE null END)
as initial_Non_Billable
FROM
individual_appointments
INNER join appointment_types on
appointment_types.id = individual_appointments.appointment_type_id
inner join invoices on
invoices.appointment_id = individual_appointments.id
inner join businesses on
businesses.id = individual_appointments.business_id
group by
businesses.label,
appointment_types.name,
appointment_types.category,
invoices.net_amount
)x
group by
ROLLUP(Clinic);
is it possible for each condition in "case when then" to have a different table?
example: for "Non_Billable" only want from individual_appointment, appointment_types, invoices and businesses table only.
while "initial_Non_Billable" only wants from individual_appointment, appointment_types, and businesses table.
can it be like that?
If possible, how? can anyone give an example?
From what I see, I suppose the problem here is that there can be many invoices per appointment, so when you join invoices you get appointments multifold which also multiplies your counts.
You don't really want to join invoices hence, but invoice sums. I.e. aggregate before joining.
Then, you are only interested in non-billable appointments, so you can put that criteria into your ON clauses and outer join in order to get clinics without such appointments.
select
coalesce(b.label, 'total') as clinic,
coalesce(sum(i.total_net_amount), 0) as initial_non_billable_sum,
count(at.id) as initial_non_billable_count,
avg(i.total_net_amount) as initial_non_billable_average
from businesses b
left join individual_appointments ia on ia.business_id = b.id
left join appointment_types at on at.id = ia.appointment_type_id
and at.category in ('others', 'non-billable')
and at.name like '%initial%'
left join
(
select appointment_id, sum(net_amount) as total_net_amount
from invoices
group by appointment_id
) i on i.appointment_id = ia.id
group by rollup(b.label)
order by b.label nulls last;

How to multiply 2 table/list in MySQL?

In MySql I obtained 2 list/tables from these 2 queries. Table 1 contains Quantities for a type of ticket, Table 2 contains Prices of a type of ticket.
Table 1:
SELECT Count(`ticket`.`Ticket_type`) AS Counter
FROM `ticket`
WHERE ((`ticket`.`Ticket_type` = 'Adult') OR (`ticket`.`Ticket_type` = 'Senior'))
GROUP BY `ticket`.`Ticket_type`
Table2 :
SELECT `ticketprice`.`price`
FROM `ticketprice`
WHERE ((`ticketprice`.`Ticket_type` = 'Adult') OR (`ticketprice`.`Ticket_type` = 'Senior'))
My question is how do I being to multiply these two tables? (Qunatity * Price) = Total
Will Appreciate any help!
Join the tables and multiply:
SELECT t.ticket_type, COUNT(*) AS quantity, p.price, p.price * COUNT(*) AS total
FROM ticket AS t
JOIN ticketprice AS p ON t.ticket_type = p.ticket_type
WHERE t.ticket_type IN ('Adult', 'Senior')
GROUP BY t.ticket_type

Create One Result set from 3 different Queries

I am trying to create a result set using three queries. I have three tables an inventory table, a form order table, and a form order detail table. I need to be able to input a date range and get how many forms were ordered, how many are in current inventory, and how many were destroyed based on if they contain a destruction date. Ultimately i want a result set that shows:
InventoryId, FormDescription, Product, Ordered, Shipped, Destroyed, Total ending
What would be the best way to get that result set using these queries?
These are my three Queries
SELECT FOD.InventoryId, SUM(FOD.FormOrderAmount) as totalOrdered, FOD.FormShippedAmount FROM tblFormOrder FMO
JOIN tblFormOrderDetails FOD ON FOD.FormOrderId = FMO.FormOrderId
WHERE FMO.OrderDateTime BETWEEN '20110101' and '20120101'
AND FMO.OrderStatus IN ('S')
GROUP BY FOD.InventoryId, FOD.FormShippedAmount -- total shipped by date and inventoryid
SELECT INV.InventoryId, SUM(INV.CurrentAmount) as currentAmount, SUM(INV.OrderAmount) as OrderAmount,
(SUM(INV.OrderAmount) - SUM(INV.CurrentAmount)) as InventoryUsed
FROM tblInventory INV
where INV.CreatedOn
BETWEEN '20110101' and '20120101'
GROUP BY INV.InventoryId -- current amount based off ordered and used
select INV.InventoryId, count(*) as total
, FMO.OrderDateTime as OrderDate, Inv.FormNo, INV.FormDescription, INV.Product
from [tblinventory] INV
join tblformorderdetails FOD ON FOD.InventoryId = inv.InventoryId
join tblformorder FMO on FMO.FormOrderId = FOD.FormOrderId
where INV.DestructionDate
BETWEEN '20110101' and '20120101'
group by
FMO.OrderDateTime,
Inv.FormNo, INV.FormDescription, INV.Product, INV.InventoryId -- using count to find how many destroyed if they have a destruction date
If you want an inner join you can do this
SELECT *
FROM (...) AS Q1, (...) AS Q2, (...) AS Q3
WHERE Q1.InventoryID = Q2.InventoryID AND Q2.InventoryID = Q3.InventoryID
This will only give you the InventoryIDs that are in all 3 queries.
You probably don't want this you probably want all inventory IDs... so you do this.
SELECT *
FROM (SELECT DISTINCT InventoryID FROM tblFormOrderDetails
UNION ALL
SELECT DISTINCT InventoryID FROM tblInventory) I
LEFT JOIN (...) Q1 ON I.InventoryID = Q1.InventoryId
LEFT JOIN (...) Q2 ON I.InventoryID = Q2.InventoryId
LEFT JOIN (...) Q3 ON I.InventoryID = Q3.InventoryId
Make each query a subquery that you join together.
SELECT *
FROM (
SELECT FOD.InventoryId, SUM(FOD.FormOrderAmount) as totalOrdered, FOD.FormShippedAmount FROM tblFormOrder FMO
JOIN tblFormOrderDetails FOD ON FOD.FormOrderId = FMO.FormOrderId
WHERE FMO.OrderDateTime BETWEEN '20110101' and '20120101'
AND FMO.OrderStatus IN ('S')
GROUP BY FOD.InventoryId, FOD.FormShippedAmount -- total shipped by date and inventoryid
) AS q1
LEFT JOIN (
SELECT INV.InventoryId, SUM(INV.CurrentAmount) as currentAmount, SUM(INV.OrderAmount) as OrderAmount,
(SUM(INV.OrderAmount) - SUM(INV.CurrentAmount)) as InventoryUsed
FROM tblInventory INV
where INV.CreatedOn
BETWEEN '20110101' and '20120101'
GROUP BY INV.InventoryId -- current amount based off ordered and used
) AS q2 ON q1.InventoryId = q2.InventoryId
LEFT JOIN (
select INV.InventoryId, count(*) as total
, FMO.OrderDateTime as OrderDate, Inv.FormNo, INV.FormDescription, INV.Product
from [tblinventory] INV
join tblformorderdetails FOD ON FOD.InventoryId = inv.InventoryId
join tblformorder FMO on FMO.FormOrderId = FOD.FormOrderId
where INV.DestructionDate
BETWEEN '20110101' and '20120101'
group by
FMO.OrderDateTime,
Inv.FormNo, INV.FormDescription, INV.Product, INV.InventoryId -- using count to find how many destroyed if they have a destruction date
) AS q3 ON q1.InventoryId = q3.InventoryId

avoid duplicate data when multiple left join

This is my sql select statement http://www.sqlfiddle.com/#!9/353e82/1,
This is the output
I use left join but the data will duplicate on output.
What I want is the data wont duplicate and only show once
Earnings
Total OT Pay - amount
Transport - amount
Uniform - amount
Deduction
Loan - amount
Unpaid Leave - amount
Thanks
If you want single row based on payslip_no then use below query-
SELECT
hrms_salary_payment.`payslip_no` AS payslip_no,
hrms_salary_payment.`payment_month` AS payment_month,
hrms_salary_payment.`basic_salary` AS basic_salary,
hrms_salary_payment.`hours_worken_week` AS hours_worken_week,
hrms_salary_payment.`ot_hours_worked` AS ot_hours_worked,
hrms_salary_payment.`total_ot_pay` AS total_ot_pay,
hrms_salary_payment.`employee_cpf` AS employee_cpf,
hrms_salary_payment.`employer_cpf` AS employer_cpf,
hrms_salary_payment.`sdl_payable` AS sdl_payable,
hrms_salary_payment.`net_salary` AS net_salary,
hrms_salary_payment.`payment_amount` AS payment_amount,
hrms_salary_payment.`payment_date` AS payment_date,
hrms_salary_additional.`item_remark` AS income,
hrms_salary_additional.`item_amount` AS income_amount,
hrms_salary_additional.`isCPF` AS isCPF,
hrms_salary_deduction.`deduction_remark` AS deduction_remark,
hrms_salary_deduction.`deduction_amount` AS deduction_amount
FROM `hrms_salary_payment`
LEFT JOIN `hrms_salary_additional` ON hrms_salary_additional.payment_id = hrms_salary_payment.payment_id
LEFT JOIN `hrms_salary_deduction` ON hrms_salary_deduction.payment_id = hrms_salary_payment.payment_id
where hrms_salary_payment.`payment_id` = 27
GROUP BY hrms_salary_payment.`payslip_no`;
But if you want 3 rows further distinquished by item_remarks then use below query-
SELECT
hrms_salary_payment.`payslip_no` AS payslip_no,
hrms_salary_payment.`payment_month` AS payment_month,
hrms_salary_payment.`basic_salary` AS basic_salary,
hrms_salary_payment.`hours_worken_week` AS hours_worken_week,
hrms_salary_payment.`ot_hours_worked` AS ot_hours_worked,
hrms_salary_payment.`total_ot_pay` AS total_ot_pay,
hrms_salary_payment.`employee_cpf` AS employee_cpf,
hrms_salary_payment.`employer_cpf` AS employer_cpf,
hrms_salary_payment.`sdl_payable` AS sdl_payable,
hrms_salary_payment.`net_salary` AS net_salary,
hrms_salary_payment.`payment_amount` AS payment_amount,
hrms_salary_payment.`payment_date` AS payment_date,
hrms_salary_additional.`item_remark` AS income,
hrms_salary_additional.`item_amount` AS income_amount,
hrms_salary_additional.`isCPF` AS isCPF,
hrms_salary_deduction.`deduction_remark` AS deduction_remark,
hrms_salary_deduction.`deduction_amount` AS deduction_amount
FROM `hrms_salary_payment`
LEFT JOIN `hrms_salary_additional` ON hrms_salary_additional.payment_id = hrms_salary_payment.payment_id
LEFT JOIN `hrms_salary_deduction` ON hrms_salary_deduction.payment_id = hrms_salary_payment.payment_id
where hrms_salary_payment.`payment_id` = 27
GROUP BY hrms_salary_payment.`payslip_no`, hrms_salary_additional.`item_remark`;

mysql left join subquery with limit gives fields with null values on parent select

I have a query with joins and subquery (derived table). If I run it without LIMIT 1 the result will contain the vat and the id field with the proper values.
The first query:
SELECT i.id, i.vat, pl.invoice_id as inv_id, pl.product_id as pl_id, pl.quantity as qty, pl.price, pl.currency, p.name, p.manufacturer, p.list_price, p.cost_price, p.wholesale_price, p.cikkszam, p.unit, p.group_name
FROM soulnsoda_products_log pl
LEFT JOIN soulnsoda_products p ON pl.product_id=p.id
LEFT JOIN (select id, vat, parent_id, beneficiary_account from soulnsoda_invoices) as i ON i.parent_id>0 AND pl.invoice_id=i.parent_id AND pl.product_id=i.beneficiary_account
WHERE pl.action=6 AND p.cikkszam = 'S6511415-BLK' AND (pl.stamp BETWEEN '2015-08-15 00:00:00' AND '2015-08-15 23:59:59') AND pl.warehouse_name='Garage - Árkád'
ORDER BY p.cikkszam
The result will contain the i.id and i.vat fields with values, but duplicating rows:
id vat inv_id pl_id qty price name
93119 27.00 93117 21961 -1.00 1096.85 HUF SUPRA ICON SX BLACK DB
93120 27.00 93117 21961 -1.00 1096.85 HUF SUPRA ICON SX BLACK DB
93119 27.00 93117 21961 -1.00 1096.85 HUF SUPRA ICON SX BLACK DB
93120 27.00 93117 21961 -1.00 1096.85 HUF SUPRA ICON SX BLACK DB
I have to filter out rows with duplicated ids. When I'm using LIMIT 1 the duplication will be gone but the id and the vat field will be NULL. And I don't know why...
The second query with LIMIT:
SELECT i.id, i.vat, pl.invoice_id as inv_id, pl.product_id as pl_id, pl.quantity as qty, pl.price, pl.currency, p.name, p.manufacturer, p.list_price, p.cost_price, p.wholesale_price, p.cikkszam, p.unit, p.group_name
FROM soulnsoda_products_log pl
LEFT JOIN soulnsoda_products p ON pl.product_id=p.id
LEFT JOIN (select id, vat, parent_id, beneficiary_account from soulnsoda_invoices LIMIT 1) as i ON i.parent_id>0 AND pl.invoice_id=i.parent_id AND pl.product_id=i.beneficiary_account
WHERE pl.action=6 AND p.cikkszam = 'S6511415-BLK' AND (pl.stamp BETWEEN '2015-08-15 00:00:00' AND '2015-08-15 23:59:59') AND pl.warehouse_name='Garage - Árkád'
ORDER BY p.cikkszam
The result will be two rows which is OK, but there are no id and vat:
id vat inv_id pl_id qty price name
NULL NULL 93117 21961 -1.00 1096.85 HUF SUPRA ICON SX BLACK DB
NULL NULL 93117 21961 -1.00 1096.85 HUF SUPRA ICON SX BLACK DB
I tried LIMIT 1, GROUP BY, MIN, MAX, etc.
I know that DISTINCT is working with my sample, but it is a simplified query to show you the problem itself.
How can I achieve the result with only two rows, but with a value in the id and vat column, using some technique in (or around) the LEFT JOIN subquery?
If your problem is duplication of rows, then use select distinct. Actually, it would be better to find the cause of the duplication, but this might be what you want:
SELECT distinct i.id, i.vat, pl.invoice_id as inv_id, pl.product_id as pl_id,
pl.quantity as qty, pl.price, pl.currency, p.name, p.manufacturer,
p.list_price, p.cost_price, p.wholesale_price, p.cikkszam, p.unit, p.group_name
FROM soulnsoda_products_log pl LEFT JOIN
soulnsoda_products p
ON pl.product_id = p.id LEFT JOIN
soulnsoda_invoices i
ON i.parent_id > 0 AND pl.invoice_id = i.parent_id AND
pl.product_id = i.beneficiary_account
WHERE pl.action = 6 AND
p.cikkszam = 'S6511415-BLK' AND
pl.stamp >= '2015-08-15' AND pl.stamp < '2015-08-16' AND
pl.warehouse_name = 'Garage - Árkád'
ORDER BY p.cikkszam
Some notes:
The left join on p is being turned into an inner join by the where clause.
The subquery is not necessary and, in fact, just hurts performance.
This version simplifies the date comparisons.
In your first query, the clause WHERE ... p.cikkszam = 'S6511415-BLK' converts your first LEFT JOIN into an ordinary inner JOIN.
ORDER BY p.cikkszam clause does nothing when combined with WHERE ... p.cikkszam = 'S6511415-BLK': there's only one value in that column of your result set.
Your timestamp end-of-interval matching is a little clumsy and almost correct. Try this, and it will be perfect.
pl.stamp >= '2015-08-15'
AND pl.stamp < '2015-08-15' + INTERVAL 1 DAY
In your second query this subquery clause only allows one, randomly chosen, invoice row to be joined to the rest of your query:
select id, vat, parent_id, beneficiary_account from soulnsoda_invoices LIMIT 1
This picks the "first" row of the invoices table. The trouble is, without ORDER BY MySQL and other RDMS engines have no solid notion of "first". So your second query isn't right.
Your query seems to ask for one row per invoice for a particular product ('S6511415-BLK') shipped (action=6) from a certain warehouse on a certain day. There are two matching rows in the invoices table in your data. There are also two matching rows in some other table. Or maybe there are four matching rows in your invoices table.
You have not described the contents of your tables to us. So it's hard for us to help you figure out where your duplicate rows are. You can use SELECT DISTINCT to eliminate the dups in your invoices table, like this, if you wish.
Here's the query I suggest. This will give you a two-row result set.
SELECT i.id, i.vat,
pl.invoice_id as inv_id, pl.product_id as pl_id, pl.quantity as qty,
pl.price, pl.currency,
p.name, p.manufacturer, p.list_price, p.cost_price,
p.wholesale_price, p.cikkszam, p.unit, p.group_name
FROM soulnsoda_products_log pl
JOIN soulnsoda_products p ON pl.product_id=p.id
LEFT JOIN (SELECT DISTINCT id, vat,
parent_id, beneficiary_account
FROM soulnsoda_invoices
) as i ON i.parent_id > 0
AND pl.invoice_id = i.parent_id
AND pl.product_id = i.beneficiary_account
WHERE pl.action=6
AND p.cikkszam = 'S6511415-BLK'
AND pl.stamp >= '2015-08-15'
AND pl.stamp < '2015-08-15' + INTERVAL 1 DAY
AND pl.warehouse_name='Garage - Árkád'
ORDER BY p.cikkszam, i.id