Query on MYSQL JOINS(2 tables) - mysql

I am new to Join tables.
I have a table 'Merchants' like the following
Merchant_Number Merchant_Name
1 ABCD
2 DEFG
I have a table 'Transactions' like the following
Merchant_Number Merchant_Name Transaction_Amount
1 ABCD 100
3 XYZ 50
1 ABCD 50
4 nnn 200
I want to join the above 2 tables such that the result is like the following
Merchant_Number Merchant_Name Transaction Amount No. Of Transactions
1 ABCD 150 2
2 DEFG 0 0
3 XYZ 50 1
4 nnn 200 1
Transaction Amount is the sum of the amount for the same merchant
No. Of transactions is the number of times the merchant used his transaction
Merchant ABCD has 2 transactions and his total transaction amount is 150
I have a query
SELECT Merchant_Name, sum(transaction_amount) as 'Transaction Amount',
count(*) as 'No. Of Transactions'
FROM Transactions
LEFT JOIN Merchants using (MERCHANT_NUMBER)
group by Merchant_Name
But this gives me only the merchants in transaction table i.e,
Merchant_Number Merchant_Name Transaction Amount No. Of Transactions
1 ABCD 150 2
3 XYZ 50 1
4 nnn 200 1
How do I query such that I have my result as explained above?
Any Help would be highly appreciated!

You want the join the other way around, this way you include all Merchants whether they have a transaction or not (which is what a LEFT JOIN does):
SELECT Merchant_Name, sum(transaction_amount) as `Transaction Amount`,
count(*) as `No. Of Transactions`
FROM Merchants
LEFT JOIN Transactions using (MERCHANT_NUMBER)
group by Merchant_Name
Of course you could instead change your query as written to a RIGHT JOIN (instead of a LEFT JOIN) but I find this just makes the code hard to read so I tend to avoid them.
Note your query as written does what you experienced; it shows all Transactions whether they have a merchant or not (FROM Transactions LEFT JOIN Merchants)
As I just noticed, your data is not normalized. You've got extra merchants in the transaction table that aren't accounted for in your merchants table. This will cause you lots of trouble.
You should find out what is doing this and prevent it, the best way is to remove the Merchant_Name column from your Transactions table altogether. If you have no control over the table designs, you should talk to someone who does :-P. Barring that, try:
SELECT Merchant_Name, sum(transaction_amount) as `Transaction Amount`,
count(*) as `No. Of Transactions`
FROM
(
SELECT Merchant_Name, Merchant_Number
FROM Merchants
UNION
SELECT Merchant_Name, Merchant_Number
FROM Transactions
) Real_Merchants
LEFT JOIN Transactions using (MERCHANT_NUMBER)
group by Merchant_Name

You almost had it, just swap Merchants and Transaction:
SELECT Merchant_Name, SUM(transaction_amount) AS 'Transaction Amount',
COUNT(*) AS 'No. Of Transactions'
FROM Merchants
LEFT JOIN Transactions using (MERCHANT_NUMBER)
GROUP BY Merchant_Name

When you want all the rows from a particular table in a LEFT JOIN you need to name that table first.
so FROM merchants LEFT JOIN transactions will give you what you want.

Change the order of your tables in the query - Merchants LEFT JOIN Transactions. Then, you will need to replace some NULL values with zeroes. Something like this:
SELECT Merchant_Name, ISNULL(SUM(transaction_amount), 0) AS 'Transaction Amount',
COUNT(*) AS 'No. Of Transactions'
FROM Merchants
LEFT JOIN Transactions using (MERCHANT_NUMBER)
GROUP BY Merchant_Name

You must normalize you database. Try this:
SELECT
MN.Merchant_Number,
MN.Merchant_Name,
ISNULL(SUM(T.Amount), 0) TransactionAmount,
COUNT(T.Merchant_Number) NoOfTransactions
FROM
(
SELECT Merchant_Number, Merchant_Name FROM Merchants
UNION
SELECT Merchant_Number, Merchant_Name FROM Transactions
) MN
LEFT JOIN Transactions T
ON MN.Merchant_Number = T.Merchant_Number
GROUP BY MN.Merchant_Number, MN.Merchant_Name

As DEFG does not appear in the left table ('Transactions') it will not appear in the result of the LEFT JOIN, which takes all the rows from the left table and finds them a match in the right. One option is to:
1. Switch between the tables and do Merchants LEFT JOIN Transactions.
2. Use COALESCE to handle the NULL values you're going to get for the right table (as you have no match for DEFG there), and replace it with 0.

Related

Adding Default Values on Joining Tables

I have the following tables:
Users
user_id course_id completion_rate
1 2 0.4
1 23 0.6
1 49 0.5
... ... ...
Courses
course_id title
1 Intro to Python
2 Intro to R
... ...
70 Intro to Flask
Each entry in the user table represents a course that the user took. However, it is rare that users have taken every course.
What I need is a result set with user_id, course_id, completion_rate. In the case that the user has taken the course, the existing completion_rate should be used, but if not then the completion_rate should be set to 0. That is, there would be 70 rows for each user_id, one for each course.
I don't have a lot of experience with SQL, and I'm not sure where to start. Would it be easier to do this in something like R?
Thank you.
You should first cross join the courses with distinct users. Then left join on this to get the desired result. If the user hasn't taken a course the completion_rate would be null and we use coalesce to default a 0.
select c.course_id,cu.user_id,coalesce(u.completion_rate,0) as completion_rate
from courses c
cross join (select distinct user_id from users) cu
left join users u on u.course_id=c.course_id and cu.user_id=u.user_id
Step1: Take the distinct client_id from client_data (abc) and do 1 on 1 merge with the course data (abc1) . 1 on 1 merge helps up write all the courses against each client_id
Step2: Merge the above dataset with the client info on client_id as well as course
create table ans as
select p.*,case when q.completion_rate is not null then q.completion_rate else 0
end as completion_rate
from
(
select a.client_id,b.course from
(select distinct client_id from abc) a
left join
abc1 b
on 1=1
) p
left join
abc q
on p.client_id = q.client_id and p.course = q.course
order by client_id,course;
Let me know in case of any queries.

group by does not work correctly join two tables

I want to get the number of tickets sold per payment method for each event. and i've the follow query:
SELECT count(distinct(a.performance_id)) as EventQuantity,
sum(a.admission_count) as TicketQuantity
FROM vw_PrecioTipoZona_Paid as a
WHERE 1 = 1
AND a.performance_id ='DED63133-A099-4949-AA57-13BBE9462BAF'
GROUP BY a.performance_id
and I get this result, which is OK:
EventQuantity TicketQuantity
1 203
But when join the table with other, the result wich is sum, in this case a.admission_count is multiplied by the number of records in the other table.
The query who has problem is this:
SELECT a.performance_id,
count(distinct(a.performance_id)) as EventQuantity,
sum(a.admission_count) as TicketQuantity,
b.payment_method as PaymentMethod
FROM vw_PrecioTipoZona_Paid as a inner join vw_Payment_UserByPerformance as b
on a.performance_id = b.performance_id
WHERE
1 = 1
and a.performance_id ='DED63133-A099-4949-AA57-13BBE9462BAF'
group by a.performance_id, b.payment_method
With this query i'm getting this result:
EventQuantity TicketQuantity PaymentMethod
1 10353 Cash
1 5887 Card
1 1624 MasterCardECommerce
1 812 VisaEcommece
And this result is wron, the result should be:
EventQuantity TicketQuantity PaymentMethod
1 111 Cash
1 63 Card
1 17 MasterCardECommerce
1 8 VisaEcommece
The vw_Payment_UserByPerformance view structure is the follow:
performance_id user_role_id userrole_name userrole_group date_transaction user_id user_name owner_user_id owner_user_name amount_adm_net amount_req_net amount_charge_charge amount_total amount_net chargeTransaction tax payment_method
And the vw_PrecioTipoZona_Paid view structure is the follow:
performance_id performance_name performance_start_date date_transaction user_role_id userrole_name userrole_group user_id user_name price_type price_zone price_zone_priority admission_count NET charge1 charge2 charge3 charge4 charge5 GROSS
Do I have to make subquery? Where is the problem here?
MySQl allows you to incorrectly use group by. You should never use the technique you used in this query.
SELECT a.performance_id,
count(distinct(a.performance_id)) as EventQuantity,
sum(a.admission_count) as TicketQuantity,
b.payment_method as PaymentMethod
FROM vw_PrecioTipoZona_Paid as a inner join vw_Payment_UserByPerformance as b
on a.performance_id = b.performance_id
WHERE
a.performance_id ='DED63133-A099-4949-AA57-13BBE9462BAF'
group by a.performance_id, b.payment_method
When you use group by the only way to guarantee correct results is to group by all the non-aggregated fields. All other databases make this part of the syntax and thus do not have this problem.
If this still does not give the correct results, then there is a problem with the specifics of what you intended vice what you wrote. We would need to see the business requirement, that table structure, the sample data in the tables and teh sample results in order to help you find the correct query.
Looking at your additional details added while I was writing this, I think you need to use a derived table.
SELECT a.performance_id,
count(a.performance_id) as EventQuantity,
a.admission_count as TicketQuantity,
b.payment_method as PaymentMethod
FROM (select performance_id, sum(admission_count) as Admissioncount vw_PrecioTipoZona_Paid
WHERE a.performance_id ='DED63133-A099-4949-AA57-13BBE9462BAF'
group by performance_id )as a
inner join vw_Payment_UserByPerformance as b
on a.performance_id = b.performance_id
group by a.performance_id, b.payment_method
You have it in your question already:
I want to get the number of tickets sold per payment method and i've
the follow query
So you're basically looking at a query like this:
SELECT payment_method, COUNT(*) FROM x GROUP BY payment_method;
You seem to be doing something differently, the results could like like so if your group on the payment_method column as well:
SELECT
a.performance_id AS performanceId,
b.payment_method AS paymentMethod,
COUNT(*) numPayments
FROM
vw_PrecioTipoZona_Paid as a
INNER JOIN
vw_Payment_UserByPerformance AS b ON (a.performance_id = b.performance_id)
WHERE
a.performance_id ='DED63133-A099-4949-AA57-13BBE9462BAF'
GROUP BY
a.performance_id,
b.payment_method
First, that doesn't make sense together:
SELECT a.performance_id,
count(distinct(a.performance_id)) as EventQuantity,
...
GROUP BY a.performance_id
You're grouping the stuff having the same performance_id, and then you're asking to show you how many DISTINCT performance_ids are in the group.
Guess what the answer always be?
Second, SQL is a declarative language, so declare what you want first.
If you can't express it in SQL adequately (which is ok) - then do it in words, and please update your question with it.
Guessing on the lack of inputs is rarely productive.

MYSQL: Multiple Table Join - Conditional on previous join

MEMBERS_TABLE
member_id
---------------------------------------------
1
ACCOUNTS_TABLE
account_id member_id
---------------------------------------------
1 1
INVESTMENTS_TABLE
investment_id account_id
---------------------------------------------
1 1
2 1
FUNDS_TABLE
fund_id investment_id
---------------------------------------------
1 1
2 2
This is my current query:
SELECT
m.member_id,
a.account_id,
i.investment_id,
f.fund_id,
COUNT(a.account_id) AS member_accounts_total,
COUNT(i.investment_id) AS member_investments_total,
COUNT(f.fund_id) AS member_funds_total
FROM members AS m
LEFT JOIN accounts AS a ON m.member_id = a.member_id
LEFT JOIN investments AS i ON a.account_id = i.account_id
LEFT JOIN funds AS f ON f.fund_id = i.fund_id
I would like to see the following results:
member_accounts_total: 1
member_investments_total: 2
member_funds_total: 2
Instead, I am getting these results:
member_accounts_total: 2
member_investments_total: 2
member_funds_total: 2
I really don't want to write multiple queries for this.
Just need to change
COUNT(a.account_id) AS member_accounts_total,
to
COUNT( distinct a.account_id) AS member_accounts_total,
The reason you're getting 2 is because the left join on accounts to investments results in 2 records. To get a distinct count of members you need to add well... distinct.
Note you may have problems with the other totals as well (Distinct may be needed there as well in the long run...) say if a member had multiple accounts. you may get odd counts as well (if each account had the same investment... would you want to see the count only once or twice?

Sum of All Related Rows with Matching ID MySQL

I have the following table schema:
tbl_portfolio
----------
id (auto number)
name
-
tbl_registration
----------------
id(auto number)
name
portfolio_id (FK to tbl_portfolio.id)
-
tbl_fund
---------
id (auto number)
registration_id (FK to tbl_registration.id)
-
tbl_transaction
---------------
id (auto number)
fund_id (FK to tbl_fund.id)
type
shares
price
I need to create a query that in psuedo-code would do the following:
SELECT port.*, SUM ALL transactions for each portfolio,
FROM tbl_portfolio port
INNER JOIN tbl_registration reg ON reg.portfolio_id = port.id
LEFT JOIN tbl_fund fund on fund.registration_id = reg.id
LEFT JOIN tbl_transaction trans ON trans.fund_id = fund.id
Now of course that query won't work...What I am needing essentially is to sum all the Price * Units for each fund, and then sum those together for each registration, and then sum all of that together for each portfolio.
Each portfolio can have multiple registrations, and each registration can have multiple funds, and each fund can have multiple transactions.
The last item that is throwing a stickler in this, there may be 10's or 100's of portfolios to count so I have no idea how to write the query, much less write it in an effective way that is not relying on subqueries that would cause it to have severely poor performance.
Thank you for the help!
Edit:
PinnyM's answer works and queries the data correctly - however I should expand on the full need.
Besides the tbl_transaction there is also a tbl_distri and tbl_div. Both have fund_id as FK to tbl_fund.id . I need to get the SUM's of tbl_distri.amount and tbl_div.units.
So the full psuedo query would be something to the effect of:
SELECT port.*, SUM ALL transactions for each portfolio, SUM(div.units), SUM(distri.amount)
FROM tbl_portfolio port
INNER JOIN tbl_registration reg ON reg.portfolio_id = port.id
LEFT JOIN tbl_fund fund on fund.registration_id = reg.id
LEFT JOIN tbl_transaction trans ON trans.fund_id = fund.id
LEFT JOIN tbl_distri distri on distri.fund_id = fund.id
LEFT JOIN tbl_div div on div.fund_id = fund.id
Have you tried using SUM()?
SELECT port.*, SUM(trans.shares * trans.price) AS transaction_totals
FROM tbl_portfolio port
INNER JOIN tbl_registration reg ON reg.portfolio_id = port.id
LEFT JOIN tbl_fund fund on fund.registration_id = reg.id
LEFT JOIN tbl_transaction trans ON trans.fund_id = fund.id
GROUP BY port.id
Judging from your question, you are looking for a rolled-up SUM
SELECT port.id AS port_id,
reg.id AS reg_id,
fund.id AS fund_id,
SUM ( trans.shares * trans.price) AS net_asset_value
FROM tbl_portfolio port
INNER JOIN tbl_registration reg ON reg.portfolio_id = port.id
LEFT JOIN tbl_fund fund on fund.registration_id = reg.id
LEFT JOIN tbl_transaction trans ON trans.fund_id = fund.id
GROUP BY port.id, reg.id, fund.id WITH ROLLUP
This will give you the sums id by id. You can use other JOIN operations with this as a subquery to fetch the textual names.
This will give results like this:
port_id reg_id fund_id net_asset_value
1 1 1 150.00
1 1 2 100.00
1 1 NULL 250.00 (rollup of previous two lines)
1 2 1 24.00
1 2 4 80.00
1 2 NULL 104.00 (rollup of previous two lines)
1 NULL NULL 354.00 (rollup at portfolio level)
3 1 1 40.00
3 1 2 50.00
3 1 NULL 90.00 (rollup of previous two lines)
3 2 1 14.00
3 2 4 60.00
3 2 NULL 74.00 (rollup of previous two lines)
3 NULL NULL 164.00 (rollup at portfolio level)
NULL NULL NULL 518.00 (grand total)
The NULLs make it into this resultset because that's what WITH ROLLUP does. This resultset only has the IDs in it; presumably the IDs are unique even if the names aren't. Non-unique names for portfolios, funds, etc, will mess up the GROUP BY pretty badly. Hence my earlier comment about retrieving the names.

3 Table Join with SUM and GROUP BY not working

I have three tables that I'm working with.
AccountingLine - Holds the generic account details
Budget - Holds the budget data for each AccountingLine (Many rows per AccountingLine)
Actual - Holds the actual cost data for each AccountingLine (Many rows per AccountingLine)
I'm trying to get the results in a single query which will return ALL ROWS from the AccountingLine table, and SUM the Amounts for each AccountingLine from the Budget and Actuals table.
Using the SQL below, the SUM isn't working for the Budget or Actual data. If I remove one of the joins and one of the SUM functions then it calculates correctly for the single joined table. Very strange... anyone run across this with multiple SUM functions on three or more tables in MySQL?
SELECT A.*, SUM(B.`amount`) AS BudgetAmount, SUM(ACT.`amount`) as ActualAmount
FROM accounting_line A
LEFT JOIN budget B ON B.accounting_line_id = A.accounting_line_id
LEFT JOIN actual ACT ON ACT.accounting_line_id = A.accounting_line_id
GROUP BY A.`accounting_line_id`
By issuing the statement above, I'd expect to see the accounting_line fields, the SUM of the Budget amounts for each accounting_line and the SUM of the Actual amounts for each accounting_line.
I've searched all over and can't find an instance of multiple SUM functions. Thanks so much for any advice.
Josh
Table Data is below:
Table: AccountingLine
act_line_id department
----------------------------------
1 Sales
2 HumanResources
Table: Budget
budget_id actg_line_id amount
----------------------------------------------
1 1 3500.00
2 2 5000.00
3 2 15000.00
Table: Actual
actual_id actg_line_id amount
----------------------------------------------
1 1 1000.00
2 2 500.00
3 2 9000.00
A join repeats each matching row in the other table. So if you have 3 rows in three tables and join them together, you end up with 9 rows. If you sum, each sum from the second and third table is 3x too high.
One solution is to sum in a subquery, so that the join only finds one row:
SELECT A.*
, B.SumAmount as BudgetAmount
, ACT.SumAmount as ActualAmount
FROM accounting_line A
LEFT JOIN
(
select accounting_line_id
, sum(amount) as SumAmount
from budget
group by
accounting_line_id
) as B
ON B.accounting_line_id = A.accounting_line_id
LEFT JOIN
(
select accounting_line_id
, sum(amount) as SumAmount
from actual
group by
accounting_line_id
) as ACT
ON ACT.accounting_line_id = A.accounting_line_id
try this modified one, calculate it's totals on a subquery
SELECT a.*, b.totalBudget, c.totalActual
FROM AccountingLine a LEFT JOIN
(
SELECT actg_line_id, SUM(amount) totalBudget
FROM Budget
GROUP BY actg_line_id
) b on a.act_line_id = b.actg_line_id
LEFT JOIN
(
SELECT actg_line_id, SUM(amount) totalActual
FROM Actual
GROUP BY actg_line_id
) c on a.act_line_id = c.actg_line_id
SQLFiddle Demo
Try this
Select A.* ,SUM(B.Amount) As BudgetAmount,SUM(Act.Amount) As ActualAmount
from AccountingLine A
INNER JOIN Budget B
ON B.budget_id = A.actg_line_id
INNER JOIN Actual Act
ON Act.actual_id = A.accounting_line_id
Grounp By A.accounting_line_id