Joining two MySQL tables, but with additional conditions? - mysql

I have two tables:
Products:
+-------------------------------------------------+
| id | name | category | price |
+-------------------------------------- ----------+
| 1 | item1 | 1 | 0.99 |
| 2 | item2 | 2 | 1.99 |
| 3 | item3 | 3 | 2.95 |
| 4 | item4 | 4 | 2.50 |
+-------------------------------------------------+
Images:
+--------------------------------------------------+
| id | file_name | p_id | priority |
+-------------------------------------- -----------+
| 1 | image1 | 1 | 0 |
| 2 | image2 | 1 | 1 |
| 3 | image3 | 2 | 2 |
| 4 | image4 | 3 | 2 |
| 5 | image5 | 3 | 3 |
| 11 | image6 | 3 | 5 |
| 16 | image7 | 4 | 1 |
| 19 | image8 | 4 | 7 |
+--------------------------------------------------+
I need to get all of product information, as well as the file name of an image for the product. Notice that a product can have more than one image; I want the one with the lowest priority. Also, I only want results for products that are in a certain category.
So, say I need information for products in categories {1,2,3}, then after the query runs the result should return:
+-----------------------------------------------------------------+
| id | name | category | price | file_name |
+-------------------------------------- ----------+---------------+
| 1 | item1 | 1 | 0.99 | image1 |
| 2 | item2 | 2 | 1.99 | image3 |
| 3 | item3 | 3 | 2.95 | image4 |
+-------------------------------------------------+---------------+
I have tried writing a couple of different join statements, but none of them work; not surprising, since I'm a total novice when it comes to SQL.
Any help would be greatly appreciated!

I will add a step by step tutorial, first getting the join right,
then adding some conditions to filter the category and finally, grouping
and using the having clause with a sub-select. You will need to use the last select
in your code. I also tested this on a mysql instance and it works.
I'm using group by in case you need some other complex stuff. It's good to have an example.
The syntax is ansii sql, it should work on all databases not just mysql
-- get everything by joining
select p.*, i.file_name
from products p
join image i on (p.id = i.p_id)
/* get everything by joining
* + filter by category
*/
select p.*, i.file_name
from products p
join image i on (p.id = i.p_id)
where p.category in (1,2,3)
/* get everything by joining
* + filter by category
* + image is the one with the lowest priority
* note: selecting the priority is not necessary
* but it's good for demonstration purposes
*/
select p.*, i.file_name, i.priority
from products p
join image i on (p.id = i.p_id)
where p.category in (1,2,3)
group by p.id
having i.priority = (select min(priority) from image where p_id = p.id)

This is the answer:
select a.id, a.name, a.category, a.price, b.filename as file_name
from products a left join (
select i.p_id, i.filename from (select id, min(priority) as min_p
from images group by p_id) q
left join images i on q.id = i.id
) b on a.id = b.p_id
where a.category in (1, 2, 3);
EXPLANATION:
First, you need to get a set where for each products with lowest priority, which is from this query:
select id, min(priority) as min_p from images group by p_id;
The result will be:
+----+----------+
| id | lowest_p |
+----+----------+
| 1 | 0 |
| 2 | 2 |
| 3 | 2 |
| 4 | 1 |
+----+----------+
4 rows in set (0.00 sec)
The next step will be to get an outer join, in this case I'd choose (arbitrarily according to my preference), the left join:
select i.p_id, i.filename from (select id, min(priority) as min_p
from images group by p_id) q left join images i on q.id = i.id;
This query produce what you want in short:
+------+----------+
| p_id | filename |
+------+----------+
| 1 | image1 |
| 2 | image3 |
| 3 | image4 |
| 4 | image7 |
+------+----------+
4 rows in set (0.00 sec)
Now you just need to decorate this, again using left join:
select a.id, a.name, a.category, a.price, b.filename as file_name
from products a left join (
select i.p_id, i.filename from (select id, min(priority) as min_p
from images group by p_id) q
left join images i on q.id = i.id
) b on a.id = b.p_id
where a.category in (1, 2, 3);
And you'll get what you want:
+------+-------+----------+-------+-----------+
| id | name | category | price | file_name |
+------+-------+----------+-------+-----------+
| 1 | item1 | 1 | 0.99 | image1 |
| 2 | item2 | 2 | 1.99 | image3 |
| 3 | item3 | 3 | 2.95 | image4 |
+------+-------+----------+-------+-----------+
3 rows in set (0.00 sec)
You can also put the products in the right hand side of the left join, depending on what you expected when there is product without images available. The query above will display the view as above, with the file_name field as "null".
On the other hand, it will not display any if you put products on the right hand side of hte left join.

Building on sarwar026's answer...
SELECT p.id, name, priority, price, file_name
FROM Products p, Images i
WHERE p.id = i.p_id
AND i.priority = (SELECT MIN(priority) FROM Images ii WHERE ii.p_id = p.id)
AND p.category IN (1,2,3)
(tested on a mysql database with copies of your tables)

Related

get one record based upon catname

I have this query, it joins two tables and give me results of all the data under one a condition CATID is
'videography'
SELECT
pm_categories_images.Image,
pm_categories_images.FileURL,
pm_categories.catname,
pm_categories.`status`,
pm_categories.sortorder,
pm_categories.parentID,
pm_categories_images.CatID
FROM
pm_categories
LEFT JOIN pm_categories_images ON pm_categories_images.CatID = pm_categories.catID
where pm_categories_images.CatID IN (select catid from pm_categories where
parentID = (select catID from pm_categories where catname = 'Videography'))
Now this videography has a results like this
http://prntscr.com/gpkuyl
now i want to get 1 record for every catname
Without a MCVE and actual requirements on which image you want from the images table and a better understanding of why you need a left join when your where clause makes it behave like an inner... and why the where clause is so complex... ...I'm really unsure what the question is after... Here's a shot... and a DEMO:http://rextester.com/CRBN50943
Sample data expected results always a plus: I made my own and several assumptions
I interperted the question as: I would like a list of the categories along with a image having the earliest alphabetic value for each category.
SELECT
CI.Image,
CI.FileURL,
C.catname,
C.`status`,
C.sortorder,
C.parentID,
CI.CatID
FROM pm_categories C
INNER JOIN pm_categories_images CI
ON CI.CatID = C.catID
INNER JOIN (SELECT Min(Image) MI, catID FROM pm_categories_images group by CATID) Z
on CI.Image = Z.MI
and CI.CatID = Z.CatId
##WHERE C.catname = 'Videography'
Order by sortOrder
Giving us
+----+------------+-----------------------------------------------+-------------+--------+-----------+----------+-------+
| | Image | FileURL | catname | status | sortorder | parentID | CatID |
+----+------------+-----------------------------------------------+-------------+--------+-----------+----------+-------+
| 1 | guid1.jpg | https://drive.google.com/BusinessID/Postings/ | Real Estate | 1 | 1 | NULL | 1 |
| 2 | guid4.jpg | https://drive.google.com/BusinessID/Postings/ | commercial | 1 | 2 | NULL | 2 |
| 3 | guid6.jpg | https://drive.google.com/BusinessID/Postings/ | Videography | 1 | 3 | NULL | 3 |
| 4 | guid10.jpg | https://drive.google.com/BusinessID/Postings/ | Other | 1 | 4 | NULL | 4 |
| 5 | guid11.jpg | https://drive.google.com/BusinessID/Postings/ | LackingMCVE | 1 | 5 | NULL | 5 |
+----+------------+-----------------------------------------------+-------------+--------+-----------+----------+-------+

conditional mysql query multiple tables

I have 2 tables in mysql database as shown below. I am looking for a query that will select * from books but if preview_image = 'none' then preview_image = the hash_id of the row with the largest size where books.id = images.parentid. Hope this makes sense.
table books
+----------------+---------------+
| id | title | preview_image |
+----------------+---------------+
| 1 | book1 | 55859076d906 |
| 2 | book2 | 20a14f9fd7cf |
| 3 | book3 | none |
| 4 | book4 | ce805ecff5c9 |
| 5 | book5 | e60a7217b3e2 |
+----------------+---------------+
table images
+-------------+------+---------------+
| parentid | size | hash_id |
+--------------------+---------------+
| 2 | 100 | 55859076d906 |
| 1 | 200 | 20a14f9fd7cf |
| 3 | 300 | 34805fr5c9e5 |
| 3 | 400 | ce805ecff5c9 |
| 3 | 500 | e60a7217b3e2 |
+--------------------+---------------+
Thanks
You can use SUBSTRING_INDEX() to obtain the first record from a sorted GROUP_CONCAT(), and switch using a CASE expression:
SELECT books.id, books.title, CASE books.preview_image
WHEN 'none' THEN SUBSTRING_INDEX(
GROUP_CONCAT(images.hash_id ORDER BY images.size DESC SEPARATOR ',')
, ',', 1)
ELSE books.preview_image
END AS preview_image
FROM books LEFT JOIN images ON images.parentid = books.id
GROUP BY books.id
Write a subquery that finds the desired hash ID for each parent ID, using one of the techniques in SQL Select only rows with Max Value on a Column. Then join this with the books table.
SELECT b.id, b.title, IF(b.preview_image = 'none', i.hash_id, b.preview_image) AS image
FROM books AS b
LEFT JOIN (SELECT i1.parentid, i1.hash_id
FROM images AS i1
JOIN (SELECT parentid, MAX(size) AS maxsize
FROM images
GROUP BY parentid) AS i2
ON i1.parentid = i2.parentid AND i1.size = i2.size) AS i
ON b.id = i.parentid

How do I select all the dealers that did not have an order?

I am trying to join two tables and only select the dealers that did not have their promo code used on any order.
How can I do this?
I'm trying this below, but it's not working right. In the example I want to get just Bob, since his promo_code hasn't been used in any orders.
SELECT d.`name`
FROM z_dealer d
LEFT OUTER JOIN z_order o ON (d.promo_code = o.promo_code)
AND o.promo_code IS NULL
Here are my tables...
mysql> select * from z_dealer;
+----+------+------------+
| id | name | promo_code |
+----+------+------------+
| 1 | John | holiday |
| 2 | Suzy | special |
| 3 | Bob | laborday |
+----+------+------------+
mysql> Select * from z_order;
+----+-------+------------+
| id | total | promo_code |
+----+-------+------------+
| 1 | 10 | holiday |
| 2 | 20 | special |
| 3 | 15 | holiday |
| 4 | 45 | special |
+----+-------+------------+
SELECT d.`name` FROM z_dealer d LEFT JOIN z_order o ON (d.promo_code = o.promo_code) WHERE o.promo_code IS NULL
Have you tried INNER JOIN? or You can try IN like this :
SELECT d.name
FROM z_dealer d
WHERE d.promo_code not in( SELECT promo_code FROM z_order)
I'm not entirely sure why it's not working in your example code. I've created the same tables locally and when I run the script you provided I get the single 'Bob' answer.
SELECT d.name
FROM z_dealer d
LEFT OUTER JOIN z_order o ON (d.promo_code = o.promo_code)
AND o.promo_code IS NULL
What results are you seeing exactly?

MySQL JOIN - Return NULL for duplicate results in left table

I believe this is a pretty simple thing, and I swear I've done it before but I can't remember how.
So let's say I have a one-to-many relationship. I want to JOIN the two tables, but not allow duplicates for the left table.
SQLFIDDLE
So based on the above SQLFiddle, my results would be:
categories.title | items.NAME | items.category_id
-----------------------------------------------------
red | apple | 1
red | car | 1
red | paper | 1
yellow | lego | 2
yellow | banana | 2
blue | pen | 3
I want it to be:
categories.title | items.NAME | items.category_id
-----------------------------------------------------
red | apple | 1
NULL | car | 1
NULL | paper | 1
yellow | lego | 2
NULL | banana | 2
blue | pen | 3
My reasoning is that this way, I can easily loop over the results without having to do any further processing with PHP.
You can replace the values with something like this:
select
case when rownum = 1 then title else null end title,
name,
category_id
from
(
SELECT c.title,
i.name,
i.category_id,
#row:=(case when #prev=title and #precat=category_id
then #row else 0 end) + 1 as rownum,
#prev:=title ptitle,
#precat:=category_id pcat
FROM items AS i
INNER JOIN categories AS c
ON c.id = i.category_id
order by i.category_id, c.title
) src
order by category_id, rownum
See SQL Fiddle with Demo
The result is:
| TITLE | NAME | CATEGORY_ID |
---------------------------------
| red | apple | 1 |
| (null) | car | 1 |
| (null) | paper | 1 |
| yellow | lego | 2 |
| (null) | banana | 2 |
| blue | pen | 3 |
It might be a long time ago when this was post. But I'll post my answer to the future readers. There is another process that is light and quick to understand.
You can make good use of variables. No subqueries are necessary.
SET #previous:="";
SELECT
IF(C.title=#previous, "", #previous:=C.title) AS Titles,
I.name, I.category_id
FROM items I
INNER JOIN categories AS C ON C.id = I.category_id
ORDER BY I.id, I.name
#previous is the variable that is being used.
SQL FIDDLE DEMO

mysql: rename all fields of a join

I didn't find anything about that.
Here's my query:
SELECT p.*, pa.*, a.*
FROM produit p
LEFT OUTER JOIN produit_attribut pa
ON p.id=pa.id_produit
LEFT OUTER JOIN attribut a
ON a.id=pa.id_attribut
ORDER BY p.id,a.id;
But the problem is that it return columns
with the same name:
+----+------------+-----+------------+-------------+------+-----+----+----+
| id | titre | desc| id_produit | id_attribut | id | desc| val| abb|
+----+------------+-----+------------+-------------+------+-----+----+----+
| 1 | Anchois | Sauc| 1 | 1 | 1 | Nomb| 2 | Nb |
| 2 | Fromage | Sauc| 2 | 1 | 1 | Nomb| 2 | Nb |
| 3 | Mozzarella | Sauc| 3 | 1 | 1 | Nomb| 2 | Nb |
| 4 | Jambon | Sauc| 4 | 1 | 1 | Nomb| 2 | Nb |
| 5 | Roquefort | Sauc| 5 | 1 | 1 | Nomb| 2 | Nb |
| 6 | Royale | Sauc| 6 | 1 | 1 | Nomb| 2 | Nb |
I'd like to know if there's a way to rename all fields of a table, something that could look like (I know the following sql doesn't work):
SELECT p.* as p*, pa.* as pa*, a.* as att*
FROM produit p
LEFT OUTER JOIN produit_attribut pa
ON p.id=pa.id_produit
LEFT OUTER JOIN attribut a
ON a.id=pa.id_attribut
ORDER BY p.id,a.id;
By the way I know that I can do something like:
SELECT
p.id AS p_id,
p.titre AS p_titre,
p.description AS p_description,
p.prix AS p_prix,
p.text_detail AS p_text_detail,
p.img_petite AS p_img_petite,
p.img_grande AS p_img_grande,
pa.id_produit AS pa_id_produit,
pa.id_attribut AS pa_id_attribut,
a.id AS a_id,
a.description AS a_description,
a.valeur AS a_valeur,
a.abbreviation AS a_abbreviation
FROM produit p
LEFT OUTER JOIN produit_attribut pa
ON p.id=pa.id_produit
LEFT OUTER JOIN attribut a
ON a.id=pa.id_attribut
ORDER BY p_id,a_id;
But I'd like to avoid this.
I'd like the request to be generic, because I'll use this request in Php and it's about a generic CRUD component (and nested table).
Is there a way?
No, there is no way to do that. However, you should try to avoid using * anyway to select columns, so with this refactoring, you get rid of both the * and can add aliases to all columns you need to add them two. Two birds with one stone! ;)