I have 2 tables that are joined using foreign key, example: budget_items table column "id" and transactions tables column "parent_id"
Each table has some similar column names such as name and date for example. So when I query the table I'm pulling the data out as follows:
SELECT
budget_items.id AS BI_id,
budget_items.owner_id as BI_owner,
budget_items.name AS BI_name,
budget_items.date AS BI_date,
budget_items.amount AS BI_amount,
budget_items.type AS BI_type,
transactions.id as TRANS_id,
transactions.parent_id as TRANS_parent,
transactions.owner_id as TRANS_owner,
transactions.amount as TRANS_amount,
transactions.date as TRANS_date,
transactions.type as TRANS_type,
transactions.processed as TRANS_processed,
transactions.name AS TRANS_name
FROM
myezbudget.budget_items budget_items,
myezbudget.transactions transactions
WHERE
budget_items.id = transactions.parent_id AND
budget_items.owner_id = ?`, [req.user.id]
My question would be, is there a better way to do this with a more concise query? I tried with a few different types of join statements but couldn't get it to output the way I liked. I'm by no means a SQL expert and would appreciate any suggestions or guidance.
I would:
Use modern JOIN syntax.
Use shorter table aliases.
The query should look like:
SELECT
i.id AS BI_id,
i.owner_id as BI_owner,
i.name AS BI_name,
i.date AS BI_date,
i.amount AS BI_amount,
i.type AS BI_type,
t.id as TRANS_id,
t.parent_id as TRANS_parent,
t.owner_id as TRANS_owner,
t.amount as TRANS_amount,
t.date as TRANS_date,
t.type as TRANS_type,
t.processed as TRANS_processed,
t.name AS TRANS_name
FROM myezbudget.budget_items i
JOIN myezbudget.transactions t on i.id = t.parent_id
WHERE i.owner_id = ?
Yes you could use an inner join instead of selecting two tables in the FROM statement and linking them together in the WHERE by doing it like this:
SELECT
budget_items.id AS BI_id,
budget_items.owner_id as BI_owner,
budget_items.name AS BI_name,
budget_items.date AS BI_date,
budget_items.amount AS BI_amount,
budget_items.type AS BI_type,
transactions.id as TRANS_id,
transactions.parent_id as TRANS_parent,
transactions.owner_id as TRANS_owner,
transactions.amount as TRANS_amount,
transactions.date as TRANS_date,
transactions.type as TRANS_type,
transactions.processed as TRANS_processed,
transactions.name AS TRANS_name
FROM
myezbudget.budget_items budget_items,
INNER JOIN myezbudget.transactions AS transactions ON budget_items.id = transactions.parent_id
WHERE
budget_items.owner_id = ?`, [req.user.id]
You could always try shortening the table aliases as well to make them more compact but if you want them descriptive then that's fine. Also when the column only exists in one table then you don't have to specify the table name all of the time, however I would say this is personal preference.
Related
I have a query that joins four table but I can't understand why its taking about a minute. I know the date between is more than 3 years. I don't really know what to do and how to optimize this query for better performance. Can someone give me suggestion on what to do? Will attached the query and the explain of the query.
SELECT
`purchase_order`.`id`,
`customer`.`name` AS customer_name,
`purchase_order`.`po_date`,
`purchase_order`.`po_number`,
`purchase_order`.`customer_id` AS customer_id ,
`customer`.`name` AS customer_name,
`purchase_order`.`status` AS po_status,
`purchase_order_items`.`product_id`,
`purchase_order_items`.`po_item_name`,
`product`.`weight` as product_weight,
`product`.`pending` as product_pending,
`product`.`company_owner` as company_owner,
`purchase_order_items`.`uom`,
`purchase_order_items`.`po_item_type`,
`purchase_order_items`.`order_sequence`,
`purchase_order_items`.`pending_balance`,
`purchase_order_items`.`quantity`,
`purchase_order_items`.`notes`,
`purchase_order_items`.`status` AS po_item_status,
`purchase_order_items`.`id` AS po_item_id
FROM purchase_order
INNER JOIN customer ON `customer`.`id` = `purchase_order`.`customer_id`
INNER JOIN purchase_order_items ON `purchase_order_items`.`po_id` = `purchase_order`.`id`
INNER JOIN product ON `purchase_order_items`.`product_id` = `product`.`id` WHERE
`purchase_order_items`.`product_id` = '121' AND
`purchase_order`.`po_date`
BETWEEN '2016-01-01' AND '2019-02-28' AND
`purchase_order_items`.`status` IN('Pending','Incomplete')
ORDER BY `purchase_order`.`po_date` DESC LIMIT 0, 20
Im also not sure what to do about the explain and Im still trying to understand why its like this and how can i optimize the query. I hope someone could help me on this.
It looks like your query is good. Rewritten using alias names for tables for shorter yet still readable "po" vs "purchase_order", "poi" vs "purchase_order_items"
Your purchase_order table should have an index at a minimum on date THEN id. So your WHERE clause can optimize based on the date, but also have the po.id as a basis to JOIN to your purchase order items table by id.
Next, the purchase order items, would help with an index on ( po_id, product_id, status ) to help optimize that join criteria.
Purchase_Order index (po_date, id )
Purchase_Order_Items index ( po_id, product_id, status )
SELECT
po.id,
c.name AS customer_name,
po.po_date,
po.po_number,
po.customer_id AS customer_id ,
c.name AS customer_name,
po.status AS po_status,
poi.product_id,
poi.po_item_name,
p.weight as product_weight,
p.pending as product_pending,
p.company_owner as company_owner,
poi.uom,
poi.po_item_type,
poi.order_sequence,
poi.pending_balance,
poi.quantity,
poi.notes,
poi.status AS po_item_status,
poi.id AS po_item_id
FROM
purchase_order po
INNER JOIN customer c
ON po.customer_id = c.id
INNER JOIN purchase_order_items poi
ON po.id = poi.po_id
AND poi.product_id = '121'
AND poi.status IN ('Pending','Incomplete')
INNER JOIN product p
ON poi.product_id = p.id
WHERE
po.po_date BETWEEN '2016-01-01' AND '2019-02-28'
ORDER BY
po.po_date DESC
LIMIT
0, 20
If that doesn't help, Your order of tables appears ok but the engine might be trying to think too much for you. You can try adding additional keyword "STRAIGHT_JOIN" (specific to mysql).
select STRAIGHT_JOIN ( ... rest of query )
This tells mySQL to run the query in the table order I gave you.
I have a mysql query to join four tables and I thought that it was just best to join tables but now that mysql data is getting bigger the query seems to cause the application to stop execution.
SELECT
`purchase_order`.`id`,
`purchase_order`.`po_date` AS po_date,
`purchase_order`.`po_number`,
`purchase_order`.`customer_id` AS customer_id ,
`customer`.`name` AS customer_name,
`purchase_order`.`status` AS po_status,
`purchase_order_items`.`product_id`,
`purchase_order_items`.`po_item_name`,
`product`.`weight` as product_weight,
`product`.`pending` as product_pending,
`product`.`company_owner` as company_owner,
`purchase_order_items`.`uom`,
`purchase_order_items`.`po_item_type`,
`purchase_order_items`.`order_sequence`,
`purchase_order_items`.`pending_balance`,
`purchase_order_items`.`quantity`,
`purchase_order_items`.`notes`,
`purchase_order_items`.`status` AS po_item_status,
`purchase_order_items`.`id` AS po_item_id
FROM `purchase_order`
INNER JOIN customer ON `customer`.`id` = `purchase_order`.`customer_id`
INNER JOIN purchase_order_items ON `purchase_order_items`.`po_id` = `purchase_order`.`id`
INNER JOIN product ON `purchase_order_items`.`product_id` = `product`.`id`
GROUP BY id ORDER BY `purchase_order`.`po_date` DESC LIMIT 0, 20
my problem really is the query that takes a lot of time to finish. Is there a way to speed this query or to change this query for faster retrieval of the data?
heres the EXPLAIN EXTENED as requested in the comments.
Thanks in advance, I really hope this is the right channel for me to ask. If not please let me know.
Will this give you the correct list of ids?
SELECT id
FROM purchase_order
ORDER BY`po_date` DESC
LIMIT 0, 20
If so, then start with that before launching into the JOIN. You can also (I think) get rid of the GROUP BY that is causing an "explode-implode" of rows.
SELECT ...
FROM ( SELECT id ... (as above) ...) AS ids
JOIN purchase_order po ON po.id = ids.id
JOIN ... (the other tables)
GROUP BY ... -- (this may be problematic, especially with the LIMIT)
ORDER BY po.po_date DESC -- yes, this needs repeating
-- no LIMIT
Something like this
SELECT
`purchase_order`.`id`,
`purchase_order`.`po_date` AS po_date,
`purchase_order`.`po_number`,
`purchase_order`.`customer_id` AS customer_id ,
`customer`.`name` AS customer_name,
`purchase_order`.`status` AS po_status,
`purchase_order_items`.`product_id`,
`purchase_order_items`.`po_item_name`,
`product`.`weight` as product_weight,
`product`.`pending` as product_pending,
`product`.`company_owner` as company_owner,
`purchase_order_items`.`uom`,
`purchase_order_items`.`po_item_type`,
`purchase_order_items`.`order_sequence`,
`purchase_order_items`.`pending_balance`,
`purchase_order_items`.`quantity`,
`purchase_order_items`.`notes`,
`purchase_order_items`.`status` AS po_item_status,
`purchase_order_items`.`id` AS po_item_id
FROM (SELECT id, po_date, po_number, customer_id, status
FROM purchase_order
ORDER BY `po_date` DESC
LIMIT 0, 5) as purchase_order
INNER JOIN customer ON `customer`.`id` = `purchase_order`.`customer_id`
INNER JOIN purchase_order_items
ON `purchase_order_items`.`po_id` = `purchase_order`.`id`
INNER JOIN product ON `purchase_order_items`.`product_id` = `product`.`id`
GROUP BY purchase_order.id DESC
LIMIT 0, 5
You need to be sure that purchase_order.po_date and all id column are indexed. You can check it with below query.
SHOW INDEX FROM yourtable;
Since you mentioned that data is getting bigger. I would suggest doing sharding and then you can parallelize multiple queries. Please refer to the following article
Parallel Query for MySQL with Shard-Query
First, I cleaned up readability a bit. You don't need tick marks around every table.column reference. Also, for short-hand, using aliases works well. Ex: "po" instead of "purchase_order", "poi" instead of "purchase_order_items". The only time I would use tick marks is around reserved words that might cause a problem.
Second, you don't have any aggregations (sum, min, max, count, avg, etc.) in your query so you should be able to strip the GROUP BY clause.
As for indexes, I would have to assume you have an index on your reference tables on their respective "id" key columns.
For your Purchase Order table, I would have an index on that based on the "po_date" in the first index field position in case you already had an index using it. Since your Order by is on that, let the engine jump directly to those dated records first and you have your descending order resolved.
SELECT
po.id,
po.po_date,
po.po_number,
po.customer_id,
c.`name` AS customer_name,
po.`status` AS po_status,
poi.product_id,
poi.po_item_name,
p.weight as product_weight,
p.pending as product_pending,
p.company_owner,
poi.uom,
poi.po_item_type,
poi.order_sequence,
poi.pending_balance,
poi.quantity,
poi.notes,
poi.`status` AS po_item_status,
poi.id AS po_item_id
FROM
purchase_order po
INNER JOIN customer c
ON po.customer_id = c.id
INNER JOIN purchase_order_items poi
ON po.id = poi.po_id
INNER JOIN product p
ON poi.product_id = p.id
ORDER BY
po.po_date DESC
LIMIT
0, 20
I've looked all over, and unfortunately, I can't seem to figure out what I'm doing wrong. I'm developing a personal financial management application that uses a MySQL server. For this problem, I have 4 tables I'm working with.
The TRANSACTIONS table contains columns CATID and BILLID which refer to primary keys in the SECONDARYCATEGORIES and BILLS tables. Both the TRANSACTIONS and BILLS tables have a column PCATID which refers to a primary key in the PRIMARYCATEGORIES table.
I'm building a SQL query that sums an "amount" column in the TRANSACTIONS table and returns the primary key from PCATID and the sum from all records that are associated with that value. If the BILLID is set to -1, it should find the PCATID in SECONDARYCATEGORIES where SECONDARYCATEGORIES.ID = TRANSACTIONS.CATID, otherwise (since -1 indicates this is NOT a bill), it should find the PCATID from the BILL record where BILLS.ID matches TRANSACTIONS.BILLID.
I'm looking for something like this (not valid SQL, obviously):
SELECT
SECONDARYCATEGORIES.PCATID,
SUM(TRANSACTIONS.AMOUNT)
FROM
TRANSACTIONS
IF (BILLID = -1) JOIN SECONDARYCATEGORIES ON SECONDARYCATEGORIES.ID = TRANSACTIONS.CATID
ELSE JOIN SECONDARYCATEGORIES ON SECONDARYCATEGORIES.ID = BILLS.CATID WHERE BILLS.ID = TRANSACTIONS.BILLID
I have tried a myriad of different JOINs, IF statements, etc, and I just can't seem to make this work. I had thought of breaking this up into different SQL queries based on the value of BILLID, and summing the values, but I'd really like to do this all in one SQL query if possible.
I know I'm missing something obvious here; any help is very much appreciated.
Edit: I forgot to describe the BILLS table. It contains a primary category, ID, as well as some descriptive data.
You can use OR in your JOIN, like this:
SELECT S.PCATID,
SUM(T.AMOUNT)
FROM TRANSACTIONS T
LEFT JOIN BILLS ON BILLS.ID = T.BILLID
JOIN SECONDARYCATEGORIES S ON (S.ID = T.CATID AND T.BILLID = -1)
OR (S.ID = BILLS.CATID AND BILLS.ID = T.BILLID)
You can also use COALESCE and CASE in your JOINs.
SELECT ID = COALESCE(s.PCATID,b.PCATID)
,Total = SUM(t.AMOUNT)
FROM TRANSACTIONS t
LEFT JOIN BILLS b ON b.BILLID = CASE WHEN t.BILLID <> -1 THEN t.BILLID END
LEFT JOIN SECONDARYCATEGORIES s ON s.CATID = CASE WHEN t.BILLID = -1 THEN t.CATID END
GROUP BY COALESCE(s.PCATID,b.BILLID)
I use UNION to pick either query. But the second query obviously won't work because it's missing BILLS table.
SELECT SECONDARYCATEGORIES.PCATID
, SUM(TRANSACTIONS.AMOUNT)
FROM TRANSACTIONS
JOIN SECONDARYCATEGORIES ON SECONDARYCATEGORIES.ID = TRANSACTIONS.CATID AND BILLID = -1
UNION
SELECT SECONDARYCATEGORIES.PCATID
, SUM(TRANSACTIONS.AMOUNT)
FROM TRANSACTIONS
JOIN SECONDARYCATEGORIES ON SECONDARYCATEGORIES.ID = BILLS.CATID AND BILLID <> -1
WHERE BILLS.ID = TRANSACTIONS.BILLID
I have this simple SQL
SELECT
(CASE
WHEN
(SELECT
Name
FROM
Customers
WHERE
Customers_id = Customer) IS NOT NULL
THEN
(SELECT
Name
FROM
Customers
WHERE
Customers_id = Customer)
ELSE Customer
END) AS Customer,
DateStart,
ProgramsRun.Programs_id,
RunInSeconds,
SwSQL,
SwProgram,
Name AS ProgramName
FROM
ProgramsRun
LEFT JOIN
Programs ON ProgramsRun.Programs_id = Programs.Programs_id;
My question is how can I avoid make two same SELECT statements: one as condition and other as a value selection. I know that I can create a temporary variable, but this forces me to use stored procedures or temporary tables. Is there a simple way to avoid this?
You could use IFNULL like this:
IFNULL((SELECT
Name
FROM
Customers
WHERE
Customers_id = Customer),Customer)
Or COALESCE like this:
COALESCE((SELECT
Name
FROM
Customers
WHERE
Customers_id = Customer),Customer)
Reference:
COALESCE(value,...)
IFNULL(expr1,expr2)
Try this
SELECT
IsNull(Name,Customer)
AS Customer,
DateStart,
ProgramsRun.Programs_id,
RunInSeconds,
SwSQL,
SwProgram,
Name AS ProgramName
FROM
ProgramsRun
LEFT JOIN
Programs ON ProgramsRun.Programs_id = Programs.Programs_id;
You can rewrite your query by using join
SELECT
COALESCE(c.Name,Customer) AS Customer,
DateStart,
ProgramsRun.Programs_id,
RunInSeconds,
SwSQL,
SwProgram,
Programs.`Name` AS ProgramName
FROM ProgramsRun
LEFT JOIN Programs ON ProgramsRun.Programs_id = Programs.Programs_id
LEFT JOIN Customers c ON c.Customers_id = Customer;
I'm using an opensource database, so it's setup is a bit over my head.
Its basically like this.
A persons normal information is in the table 'person_per'
There is custom information in the table 'person_custom'
both use 'per_ID' to organize.
select per_ID from person_custom where c3 like '2';
gives my the IDs of people who fit my search, I want to "join" (I think) their name, phone, ect from the 'person_per' table using the ID as the "key"(terms I read that seem to fit).
How can I do that in a single query?
select per.*
from person_per per
inner join person_custom cus on cus.per_id = per.per_id
where cus.c3 = 2
You can retrieve all the columns from both tables with a single query:
SELECT p.name
, p.phone
, p.ect
, c.custom_col
FROM person_per p
JOIN person_custom c
ON c.per_ID = p.per_ID
WHERE c.c3 LIKE '2'
Use a JOIN operator between the table names, and include the "matching" criteria (predicate) in the ON clause.