Need to join MySql query to 3 tables - mysql

SELECT products.acctnum,products.subacctnum,NOW(),
items.amount,items.id,items.invoice_id,items.product_id,
items.po_id, invoices.customer_id, purchaseorders.vendor_id FROM items
INNER JOIN (products, invoices, purchaseorders)
ON (items.product_id=products.product_id AND items.invoice_id=invoices.id
AND items.po_id=purchaseorders.id)
This returns nothing... however..
SELECT products.acctnum,products.subacctnum,NOW(),
items.amount,items.id,items.invoice_id,items.product_id,
items.po_id, purchaseorders.vendor_id FROM items
INNER JOIN (products, purchaseorders)
ON (products.product_id=items.product_id AND purchaseorders.id=items.po_id)
Works...
SELECT products.acctnum,products.subacctnum,NOW(),
items.amount,items.id,items.invoice_id,items.product_id,
items.po_id, invoices.customer_id FROM items
INNER JOIN (products, invoices)
ON (products.product_id=items.product_id AND invoices.id=items.invoice_id)
Works...
Works for the rows I need in the result but when I join the 3rd table it doesn't work. LEFT JOIN displayed all the columns I needed but some rows were NULL.

I imagine the join clause you want looks more like this:
FROM items LEFT JOIN
invoices
ON invoices.id = items.invoice_id LEFT JOIN
purchaseorders
ON purchaseorders.id = items.po_id LEFT JOIN
products
ON products.product_id = items.product_id
I'm not sure which fields are not valid when you select them, but you can probably fix such issues by using coalesce() with appropriate fields from invoices and purchaseorders.

Related

SQL Left Join a Table on a Left Joined Table

Iam currently trying to left join a table on a left joined table as follows.
I have the tables:
accounts (id, vorname, nachname)
projektkurse (id, accounts_id, projektwochen_id)
projektkurs_einzel (id, projektkurse_id)
projektkurs_einzel_zeiten (id, date, shift, projektkurs_einzel_id)
Now I want to get every account and the amount times they have an entry inside of projektkurs_einzel_zeiten, which should also be unique. So having the same date and shift multiple times does not count as multiple entries. The result should also be limited by the column projektwochen_id from the table projektkurse. This column should match a certain value for example 8.
Some Accounts don't have any entries in projektkurse, projektkurs_einzel and projektkurs_einzel_zeiten, this is why my first thought was using LEFT JOIN like this:
SELECT accounts.id, accounts.vorname, accounts.nachname, COUNT(DISTINCT projektkurs_einzel_zeiten.date, projektkurs_einzel_zeiten.shift) AS T
FROM accounts
LEFT JOIN projektkurse on accounts.id = projektkurse.creator_id
LEFT JOIN projektkurs_einzel on projektkurse.id = projektkurs_einzel.projektkurs_id
LEFT JOIN projektkurs_einzel_zeiten ON projektkurs_einzel.id = projektkurs_einzel_zeiten.projektkurs_einzel_id
WHERE projektkurse.projektwochen_id = 8
GROUP BY accounts.id
This query does not achieve exactly what I want. It only returns accounts that have atleast one entry in projektkurse even if they have none in projektkurs_einzel and projektkurs_einzel_zeiten. The Count is obviously 0 for them but the accounts that have no entries in projektkurse are being ignored completly.
How can I also show the accounts that don't have entries in any other table with the Count 0 aswell?
I would recommend writing the query like this:
SELECT a.id, a.vorname, a.nachname,
COUNT(DISTINCT pez.date, pez.shift) AS T
FROM accounts a LEFT JOIN
projektkurse
ON a.id = pk.creator_id AND
pk.projektwochen_id = 8 LEFT JOIN
projektkurs_einzel pe
ON pk.id = pe.projektkurs_id LEFT JOIN
projektkurs_einzel_zeiten pez
ON pe.id = pez.projektkurs_einzel_id
GROUP BY a.id, a.vorname, a.nachname;
Notes:
Your problem is fixed by moving the WHERE condition to the ON clause. Your WHERE turns the outer join into an inner join, because NULL values do not match.
Table aliases make the query easier to write and to read.
It is a best practice to include all unaggregated columns in the GROUP BY. However, assuming that id is unique, your formulation is okay (due to something called "functional dependencies").
You should not use eft join table's column ins where condition this work as inner join
You should move the where condition for a left joined table in the corresponding ON clause
SELECT accounts.id, accounts.vorname, accounts.nachname, COUNT(DISTINCT projektkurs_einzel_zeiten.date, projektkurs_einzel_zeiten.shift) AS T
FROM accounts
LEFT JOIN projektkurse on accounts.id = projektkurse.creator_id
AND projektkurse.projektwochen_id = 8
LEFT JOIN projektkurs_einzel on projektkurse.id = projektkurs_einzel.projektkurs_id
LEFT JOIN projektkurs_einzel_zeiten ON projektkurs_einzel.id = projektkurs_einzel_zeiten.projektkurs_einzel_id
GROUP BY accounts.id

Inner Join for list of records NOT in a second table (NOT Inner Join)

I have a table let's call it products with a list of Manufacturers and Products.
I have a second table let's call it Customer, Orders.
I can do a join to make a list of all the items from each manufacturer the customer ordered doing an Inner Join. Yet trying to do an Inner Join for the items they did not fails.
I tried an Inner Join with 'Orders.Product != Products.Product' but that only works where the Customer has one order. Once there is more than one order I get the same list I would have doing an Inner Join. Any thoughts? I'll try to make a SqlFiddle tonight but was hoping a quick description might help a MySql / Join expert who has done 'NOT Inner Join'before...
It is called an anti join, you can use left join with is null check:
select p.*
from products p
left join orders o on p.Product = o.Product
where o.product is null

Speed up an SQL query which uses a FullText Join

I have a situation where I need to JOIN and inner query from a products database, grouping items by name and then using the name column to perform the join, thus:
SELECT p.*, p3.variants, sl.id, l.name as locationName FROM products p
LEFT JOIN (SELECT item, count(DISTINCT id) as variants FROM products GROUP BY item) p3
ON p3.item = p.item
LEFT JOIN stockLevels sl ON sl.id=p.id
LEFT JOIN locations l ON l.id=sl.stockLocation AND l.showStock='1'
WHERE p.id='10459' GROUP BY p.id
Having messed around with the order of JOIN and the query itself, I have determined that having this inner table being created first and performing the first required join makes a difference to the speed of the query, but the inner join (SELECT item, count(DISTINCT id) as variants FROM products GROUP BY item) is giving (at present) 7034 rows, which is needless as only about 5 are required.
If I modify the inner join to:
(SELECT item, count(DISTINCT id) as variants
FROM products *WHERE item LIKE '%SOME VALUE%'* GROUP BY item)
This obviously reduces the number of rows returned by that inner join, and it is roughly twice as fast.
But, I can't do this as I don't have a known value to use for the inner where clause.
Is there any way I can bring the results from the outer table into the inner and produce an inner query that references the outer, ie:
SELECT p.*, p3.variants, sl.id, l.name as locationName FROM products p
LEFT JOIN (SELECT item, count(DISTINCT id) as variants
FROM products *WHERE item LIKE p.item * GROUP BY item) p3 << New Where Clause
ON p3.item = p.item
LEFT JOIN stockLevels sl ON sl.id=p.id
LEFT JOIN locations l ON l.id=sl.stockLocation AND l.showStock='1'
WHERE p.id='10459' GROUP BY p.id
I know that what I have written above won't work, and understand why, but is there a way I could achieve something on these lines?
With the number of records in my database at the moment, this actually isn't an issue, the data is returned very quickly, but I can imagine that in time, when the data grows, it could be.
Note, I did look at making a parentId integer column, instead of grouping by the FullText item but there are lots of other issues within my application, and it didn't seem to speed the query up anyway, for the same reasons as listed above, the inner still had to return all rows
Also Note, this is MySQL
If it helps at all here's the output from EXPLAIN
You can put a correlated subquery in the SELECT clause:
SELECT p.*, sl.id, l.name as locationName,
(SELECT COUNT(distinct id)
FROM products p3
WHERE p3.item = p.item) as variants
FROM products p
LEFT JOIN stockLevels sl ON sl.id=p.id
LEFT JOIN locations l ON l.id=sl.stockLocation AND l.showStock='1'
WHERE p.id='10459' GROUP BY p.id
If only 5 are required, just do it the classic way:
SELECT
p.*,
p3.variants,
sl.id,
l.name as locationName,
(SELECT count(DISTINCT id)
FROM products
WHERE item LIKE p.item GROUP BY item) total
FROM products p
LEFT JOIN stockLevels sl
ON sl.id=p.id
LEFT JOIN locations l
ON l.id=sl.stockLocation AND l.showStock='1'
WHERE p.id='10459' GROUP BY p.id
Here you can access the parent table.

AVG + Inner join

I'm trying to make something like a chart list and my actual query is like this:
SELECT
user.username,
songs.*,
albums.desc,
albums.release,
albums.name,
AVG(songrating.rating)
FROM
songs
INNER JOIN
user
ON
songs.userid=user.id
INNER JOIN
albums
ON
songs.albumid=albums.id
INNER JOIN
songrating
ON
songs.id=songrating.songid
GROUP BY
songrating.songid
it only shows entries with at least one rating entry, but I also want these without a rating
I tried to use it with if / case but it doesn't work (or I'm doing something wrong)
If I remove the avg etc, it works correctly.
It may be JOIN issue
The INNER JOIN keyword selects all rows from both tables as long as there is a match between the columns in both tables.
The LEFT JOIN keyword returns all rows from the left table (table1), with the matching rows in the right table (table2). The result is NULL in the right side when there is no match.
Try LEFT JOIN

MySQL query: Slow response when joining table using ifnull

I have three tables, products, variants, and data.
Each product has it's own productcode. Products can have any number of variants including none. Variants have their own code variantcode. Products have a productid column, and each variant has a related productid column.
I want a list of all the codes, but I only care about the productcode if a product has no variants. Otherwise I want the variantcode. I wrote this query to get this list:
SELECT IFNULL(variants.variantcode, products.productcode) AS code
FROM products
LEFT OUTER JOIN variants ON (products.productid = variants.productid)
ORDER BY code
This query works as I expected.
The data table contains extra data for each code. I want to join this data onto this list. I tried this query:
SELECT IFNULL(variants.variantcode, products.productcode) AS code
FROM products
LEFT OUTER JOIN variants ON (products.productid = variants.productid)
LEFT OUTER JOIN data ON (data.partno = code)
ORDER BY code
But I get an error "Unknown column 'code' in 'on clause'". I assumed this had something to do with code being a generated value, so I then tried this query:
SELECT IFNULL(variants.variantcode, products.productcode) AS code
FROM products
LEFT OUTER JOIN variants ON (products.productid = variants.productid)
LEFT OUTER JOIN data ON (data.partno = IFNULL(variants.variantcode, products.productcode))
ORDER BY code
This query worked, but took a long time (~20 seconds vs <1 second for the first query). Is the IFNULL in the ON clause the problem? How can I speed it up?
Try this:
select product_variants.code from
(SELECT IFNULL(variants.variantcode, products.productcode) AS code
FROM products
LEFT OUTER JOIN variants ON (products.productid = variants.productid)
ORDER BY code)
as product_variants
LEFT OUTER JOIN data ON (data.partno = product_variants.code)
One thing you can try is to join to your data table twice, something like this:
SELECT IFNULL(variants.variantcode, products.productcode) AS code
, IFNULL(D1.something, D2.something) AS something
FROM products
LEFT OUTER JOIN variants
ON (products.productid = variants.productid)
LEFT OUTER JOIN data as D1
ON (D1.partno = variants.variantcode)
LEFT OUTER JOIN data as D2
ON (D2.partno = products.productcode)
ORDER BY code