mysql understanding group by with a joined table - mysql

So I have the "main" table (A) with fields: id, order_number, order_name and table (B) with fields: id, fk.order_number, tracking_number
Table (A) is responsible for keeping track of each order, while table (B) stores all associated tracking information per each order.
What I am trying to accomplish is to query each order from table A and join table B to show the first tracking number that has been stored for each order, almost like a limit 1 (return only the first stored tracking number for each order).
How I am doing this currently is a join between table A and table B on the order_number field, but I am using the GROUP BY tableA.order_number at the end of the statement.
select tablea.order_number, tablea.order_name, tableb.tracking_number
from tablea
join table b
on tablea.order_number = tableb.order_number
group by tablea.order_number
I guess the question revolves around, what is the default group by ordering when you return multiple rows back from the joined table?
For example, in table A, there is only 1 row, while in tale B there are 2 rows (2 tracking numbers for the order). So, when I group by in this case, does that always take the first match from the joined table where the condition matches the group by? If I removed group by, 2 rows would be returned.
I realize what is happening because I have the group by condition on tableA, and so it only shows the first row because both rows returned from the join have the same order number, which appears to be what I want (limit 1 tracking number per order), but I'm not sure if programmatically I actually did this correctly or if it happens this way because that is how the group by clause works and how I used it here. I just want to limit 1 tracking number from the tableB based on the order_number of table A.
Updated (with example query)
SELECT m.message_id, m.message_date, m.message_order_number, m.message_purchase_order, m.message_vendor_invoice, ve.vendor_email_display, concat(c.customer_first_name, ' ', c.customer_last_name) as customer_name,
min(ti.tracking_information_id) as tracking_information_id, ti.tracking_information_tracking_number, ti.tracking_information_tracking_number_status
FROM email.message m
JOIN email.customer c
ON m.message_tagged_customer_first = c.customer_id and m.message_tagged_customer_last = c.customer_id
JOIN vendor_email ve
ON m.message_sender = ve.vendor_email_id
LEFT JOIN tracking_information ti
ON m.message_order_number = ti.tracking_information_order_number
group by m.message_order_number
In this case, I want to return all information in message table, and the first matching row in table tracking

Group byis for aggregation function as MIN() ,, MAX() , COUNT() . .. and is for define respect which column the aggreagtion function must be performed..
If you are looking for a specific ordered result you should use ORDER BY that work for the columns value as is
select tablea.order_number, tablea.order_name, tableb.tracking_number
from tablea
join table b on tablea.order_number = tableb.order_number
order by tablea.order_number

Related

mysql left join with group_concat - only shows a single result

So I am not sure if I am taking the right approach but here is what I am after:
I need to get all the records from table A
Then join on table B and concatenate all the values that match a specific ID from Table.
What I am noticing with my query below, is that I only get results where there is a record in Table B - I want to be able to display a NULL value in my result set if there is no corresponding value in Table A
SELECT Account.AccountID, AccountOpenedDate, AccountStatus, GROUP_CONCAT(Expense.ExpenseType SEPARATOR ':') AS Expense FROM Account
JOIN Expense ON Account.AccountID=Expense.AccountID
GROUP BY MONTH(DATE(AccountOpenedDate)), Account.AccountID
ORDER BY Account.AccountID ASC;
I want to return all accounts and account status and opened date
Then if Expense has a value for that row display those values concatenated with ":" as a separator.
I only seem to get results where a record exists in both tables.
You are describing a left join:
select
a.accountID,
a.accountOpenedDate,
a.accountStatus,
group_concat(e.expenseType separator ':') as expense
from account a
left join expense e on e.accountID = a.accountID
group by a.accountID
order by a.accountID
I also don't see the point for expression MONTH(DATE(AccountOpenedDate)) in the GROUP BY clause: you seem to want one row per account, so this seems irrelevant.
The above query works under the assumption that accountID is the primary key of table account: other columns from the same column are functionaly dependent on the primary key, so you do not need to list them in the group by clause. You could also write this as:
select
a.accountID,
a.accountOpenedDate,
a.accountStatus,
group_concat(e.expenseType separator ':') as expense
from account a
left join expense e on e.accountID = a.accountID
group by a.accountID, a.accountOpenedDate, a.accountStatus
order by a.accountID
Side notes:
table aliases make the query easier to write and read
in a multi-table query, all columns should be qualified (prefixed) with the (alias of the) table they belong to

Create view from two tables with subtract operation

I'm almost new with SQL syntax and I need help to create a view on MySQL.
I have a table with a PK column called ID, a column called total_seats and another one is title.
In the second table, I have multiple rows, with a firstname column and a FK that corresponds to the PK (total_seats) present in the first table.
I need to create a view where I can calculate the available_seats (total_seats minus occurrence in the second table) for each element present in the first table.
Actually I'm calculating the "occupied" seats but the join give me the result only for already taken event, so the result is that I don't see the available_seats for the empty event.
SELECT b.ID_event, a.*,
COUNT(*) AS occupied FROM second_table b
LEFT JOIN first_table a ON b.ID_event = a.ID
GROUP BY ID_event
You could subtract the count
select a.ID_event, a.total_seats, count(*) as occupied, a.total_seats - count(*) difference
from first_table a
left join second_table ba ON b.ID_event = a.ID
group by a.ID_event, a.total_seats

Get a row which has the maximum id

Problem in simple words, 1st must be left joined to the 2nd table where the record is latest. So, I use an approach of using function MAX()
Currently I have 2 tables.
matches
matches_payments
|
Now I want to join the second table to first one using MAX(id) on matches_payments
Desired result
but I am not getting desired result due to greatest-n-per-group problem.
Query
SELECT matches.id, mp.*
FROM matches
LEFT JOIN (SELECT
MAX(id) AS id,
match_id
paymentStatus
FROM matches_payments
GROUP BY match_id) AS mp ON mp.id = matches.id;
Desired result is not produced due to : Stackoverflow Question
When using this feature, all rows in each group should have the same values for the columns that are ommitted from the GROUP BY part. The server is free to return any value from the group, so the results are indeterminate unless all values are the same.
FROM MySQL Dev
PS : I know the tables are poorly designed. It is not my work as the last developer did those.
You need two joins. You need a self-join of the matches_payments table to get the row with the highest ID for each match_id, as shown in SQL Select only rows with Max Value on a Column. Then you LEFT JOIN this to matches to combine the two tables:
SELECT m.*, mp.paymentStatus, mp.paymentAmount
FROM matches AS m
LEFT JOIN (
SELECT mp1.*
FROM matches_payments AS mp1
JOIN (SELECT match_id, MAX(id) AS id
FROM matches_payments
GROUP BY match_id) AS mp2
ON mp1.match_id = mp2.match_id AND mp1.id = mp2.id
) AS mp ON mp.match_id = m.id

How can I select data from one table depending on the data from another table

I have 2 tables: contracts_main_list and contracts_detail.
In contracts_main_list I have columns:
user_id
contract_id
and in contracts_detail:
contract_id
other columns with data
I need to select all the rows from the table contracts_main_list WHERE user_id = some number.
From these rows I need to get the list of contract numbers (from column contract_id) and according to them select rows corresponding to each of the contract number from the list. So something like:
WHERE contracts_detail.contract_id = contracts_main_list.contract_id
The contract_ids are probably gonna be unique, but in case there is some kind of error and there will be more rows with the same contract_id in either of the tables, I need to select only one row (so probably using DISTINCT) and select the latest record (both tables have a column id as a primary key)
Also if there is no row in contracts_detail matching with the contract_id to the contract_id of the first table contracts_main_list it should skip the row. But I guess the condition:
WHERE contracts_detail.contract_id = contracts_main_list.contract_id
already covers it.
I hope I made it clear enough. What I am trying to do in real life is show list of contracts with all the relevant data belonging to the user.
To sum this up, I need to find all the contracts belonging to the user and select the rows with details about each contract and finally get the data from the contracts_detail table as a result.
Here is the result you're looking for:
SELECT CD.*
FROM (SELECT C2.contract_id
,MAX(C2.id) AS last_main_list_id
,MAX(CD2.id) AS last_contracts_detail_id
FROM contracts_main_list C2
INNER JOIN contracts_detail CD2 ON CD2.contract_id = C2.contract_id
GROUP BY C2.contract_id) L
INNER JOIN contracts_main_list C ON C.id = L.last_main_list_id
AND C.user_id = ?
INNER JOIN contracts_detail CD ON CD.id= L.last_contracts_detail_id
This query use a subquery for the FROM because of the following indication you provided:
The contract_ids are probably gonna be unique, but in case there is
some kind of error and there will be more rows with the same
contract_id in either of the tables, I need to select only one row
If you're sure that the contract_id are unique, here is the same query without this check on contract_id:
SELECT CD.*
FROM contracts_main_list C
INNER JOIN contracts_detail CD ON CD.contract_id = C.contract_id
WHERE C.user_id = ?
Hope this will help you.

Select corresponding records from another table, but just the last one

I have 2 tables authors and authors_sales
The table authors_sales is updated each hour so is huge.
What I need is to create a ranking, for that I need to join both tables (authors has all the author data while authors_sales has just sales numbers)
How can I create a final table with the ranking of authors ordering it by sales?
The common key is the: authorId
I tried with LEFT JOIN but I must be doing something wrong because I get all the authors_sales table, not just the last.
Any tip in the right direction much appreciated
If you're looking for aggregate data of the sales, you'd want to join the tables, group by the authorId. Something like...
select authors.author_id, SUM(author_sales.sale_amt) as total_sales
from authors
inner join author_sales on author_sales.author_id = authors.author_id
group by authors.author_id
order by total_sales desc
However (I couldn't distinguish from your question whether the above scenario or next is true), if you're only looking for the max value of the author_sales table (if the data in this table is already aggregated), you can join on a nested query for author_sales, such as...
select author.author_id, t.sales from authors
inner join
(select top 1 author_sales.author_id,
author_sales.sale_amt,
author_sales.some_identifier
from author_sales order by some_identifier desc) t
on t.author_id = author.author_id
order by t.sales desc
The some_identifier would be how you determine which record is the most recent for author_sales, whether it is a timestamp of when it was inserted or an incremental primary key, however it is set up. Depending on if the data in author_sales is aggregated already, one of these two should do it for you...
select a.*, sum(b.sales)
from authors as a
inner join authors_sales as b
using authorId
group by b.authorId
order by sum(b.sales) desc;
/* assuming column sales = total for each row in authors_sales */