I am new to SQL and have the following challenge, I have a table on the left that has products assigned to a single company and denoted by a ref_id (it comes from another table, and I have simplified it for sake of question) and I have a master list. I want to compare all the products for company "A" against the master list and show all the products company A has not subscribed to. I am using JOIN and end up getting duplicate column names which throws my PHP program off. Here is what I have tried several syntax for UNION, RIGHT JOIN, LEFT JOIN and NOT operators, and the below is the closest I have gotten, however I am getting duplicate column names and I only need the last table with the last 2 columns. Please advise.
mysql> select ref_id, product_id, short_name from ap_company_product order by ref_id, product_id;
+--------+------------+------------+
| ref_id | product_id | short_name |
+--------+------------+------------+
| 2 | 10 | product 10 |
| 2 | 11 | product 11 |
| 2 | 12 | product 12 |
| 2 | 15 | product 15 |
| 2 | 17 | product 17 |
| 2 | 21 | product 21 |
| 3 | 11 | product 11 |
| 3 | 13 | product 13 |
| 3 | 17 | product 17 |
| 3 | 20 | product 20 |
+--------+------------+------------+
10 rows in set (0.00 sec)
THE MAIN LIST
mysql> select product_id, short_name from ap_company_product_list;
+------------+-------------+
| product_id | short_name |
+------------+-------------+
| 10 | product 10 |
| 11 | product 11 |
| 12 | product 12 |
| 13 | product 13 |
| 14 | product 14 |
| 15 | product 15 |
| 16 | product 16 |
| 17 | product 17 |
| 18 | product 18 |
| 19 | product 19 |
| 20 | product 20 |
| 21 | product 21 |
| 22 | product 22 |
+------------+-------------+
13 rows in set (0.00 sec)
Another SQL which is the actual results I need
SELECT aa.product_id as product_id, aa.short_name as short_name, aa.ref_id as ref_id, bb.product_id as product_id, bb.short_name as short_name FROM (select ref_id, product_id, short_name from ap_company_product where ref_id=2) aa RIGHT OUTER JOIN ap_company_product_list bb ON aa.product_id = bb.product_id where aa.product_id is NULL;
+------------+------------+--------+------------+------------+
| product_id | short_name | ref_id | product_id | short_name |
+------------+------------+--------+------------+------------+
| NULL | NULL | NULL | 13 | product 13 |
| NULL | NULL | NULL | 14 | product 14 |
| NULL | NULL | NULL | 16 | product 16 |
| NULL | NULL | NULL | 18 | product 18 |
| NULL | NULL | NULL | 19 | product 19 |
| NULL | NULL | NULL | 20 | product 20 |
| NULL | NULL | NULL | 22 | product 22 |
+------------+------------+--------+------------+------------+
7 rows in set (0.01 sec)
Just use this:
SELECT bb.product_id as product_id, bb.short_name as short_name FROM (select ref_id, product_id, short_name from ap_company_product where ref_id=2) aa RIGHT OUTER JOIN ap_company_product_list bb ON aa.product_id = bb.product_id where aa.product_id is NULL;
BTW, I would prefer this one:
SELECT product_id, short_name FROM ap_company_product_list WHERE product_id NOT IN (SELECT product_id FROM ap_company_product);
Related
I'm creating e-commerce web site using MySQL. I have successfully created and inserted data to database.
Here is my database schema
table: categories table: product_types
+----+--------------+ +----+-------------+------------+
| id | name | | id | category_id | name |
+----+--------------+ +----+-------------+------------+
| 1 | Electronics | | 1 | 1 | Smartphone |
| 2 | Fashion | | 2 | 1 | Speakers |
+----+--------------+ +----+-------------+------------+
table: products
+----+-----------------+-------------+-------------------+-------+
| id | product_type_id | category_id | name | price |
+----+-----------------+-------------+-------------------+-------+
| 1 | 1 | 1 | Samsung Galaxy A3 | 300 |
| 2 | 1 | 1 | Samsung Galaxy A7 | 400 |
+----+-----------------+-------------+-------------------+-------+
table: options table: option_values
+----+-----------------+-------+ +----+-----------+------------+
| id | product_type_id | name | | id | option_id | name |
+----+-----------------+-------+ +----+-----------+------------+
| 1 | 1 | RAM | | 1 | 1 | 512 MB |
| 2 | 1 | Screen| | 2 | 1 | 1 GB |
| 3 | 1 | OS | | 3 | 3 | Android 5 |
+----+-----------------+-------+ | 4 | 3 | Android 6 |
| 5 | 2 | HD |
| 6 | 2 | FHD |
+----+-----------+------------+
table: product_option_values
+----+------------+-----------+-----------------+
| id | product_id | option_id | option_value_id |
+----+------------+-----------+-----------------+
| 15 | 1 | 1 | 1 |
| 16 | 1 | 2 | 5 |
| 17 | 1 | 3 | 3 |
| 18 | 2 | 1 | 2 |
| 19 | 2 | 2 | 6 |
| 20 | 2 | 3 | 4 |
+----+------------+-----------+-----------------+
Search must trigger through name column of each table and return name and price from products table.
The problem is that I don't know how to perform full text search joining all that tables.
Is there any easy way to do it?
You need a query that LEFT JOINs on each table to search with a condition based on fulltext search function MATCH, with a WHERE clause to filter out non-matching records. The SELECT DISTINCT ensures that you will not see duplicates.
We need to adjust manually the JOIN criteria from each table to products : option_values is the most complicated case as it does not directly references products (an additional join on product_option_values is needed, aliased pov below.
SELECT DISTINCT p.name, p.price
FROM
products p
LEFT JOIN categories c
ON MATCH(c.name) AGAINST('foo' IN NATURAL LANGUAGE MODE)
AND c.id = p.category_id
LEFT JOIN product_types pt
ON MATCH(pt.name) AGAINST('foo' IN NATURAL LANGUAGE MODE)
AND pt.category_id = p.category_id
LEFT JOIN options o
ON MATCH(o.name) AGAINST('foo' IN NATURAL LANGUAGE MODE)
AND o.product_type_id = p.product_type_id
LEFT JOIN product_option_values pov
ON pov.product_id = p.id
LEFT JOIN option_values ov
ON MATCH(ov.name) AGAINST('foo' IN NATURAL LANGUAGE MODE)
AND ov.id = pov.option_value_id
WHERE
COALESCE(c.id, pt.id, o.id, ov.id) IS NOT NULL
I have two tables: products and meta.
Products table:
+----+----------+
| id | name |
+----+----------+
| 1 | TV |
| 2 | Computer |
| 3 | Freezer |
+----+----------+
Meta table:
+----+------------+-----------+------------+
| id | product_id | meta_key | meta_value |
+----+------------+-----------+------------+
| 1 | 1 | currency | USD |
| 2 | 1 | price | 1100 |
| 3 | 2 | currency | PLN |
| 4 | 2 | price | 9300 |
| 5 | 3 | currency | USD |
| 6 | 3 | price | 1200 |
+----+------------+-----------+------------+
now the following query works fine:
select price.product_id, products.name, price.meta_value as 'price', currency.meta_value as 'currency'
from meta as price
join meta as currency on(price.product_id=currency.product_id and currency.meta_key='currency')
join products on(products.id=price.product_id)
where price.meta_key='price';
result:
+------------+----------+-------+----------+
| product_id | name | price | currency |
+------------+----------+-------+----------+
| 1 | TV | 1100 | USD |
| 2 | Computer | 9300 | PLN |
| 3 | Freezer | 1200 | USD |
+------------+----------+-------+----------+
but the query:
select price.product_id, products.name, price.meta_value as 'price', currency.meta_value as 'currency'
from meta as price, meta as currency
join products on(products.id=price.product_id)
where
price.product_id=currency.product_id
and price.meta_key='price'
and currency.meta_key='currency';
returns: "Unknown column 'price.product_id' in 'on clause'"
Why does that happen ?
Your "from" clause is interpreted as:
from meta as price, (meta as currency join products on (products.id = price.product_id)
So, there is no price.product_id available to the on clause, as it only knows about the meta as currency and products tables.
For this question I have created a simple example that illustrates what I am asking.
Say I had a table called 'books'
+----+----------------------------+-----------+
| pk | title | author_id |
+----+----------------------------+-----------+
| 1 | The Lost Symbol | 1 |
| 2 | Follow Us Home | 2 |
| 3 | The Man in the High Castle | 3 |
+----+----------------------------+-----------+
(table a)
And another table called 'shops', that had a list of shops that sold each book:
+----+---------+-------------+-------+
| pk | book_id | shop_name | price |
+----+---------+-------------+-------+
| 1 | 1 | WHSmith | 5.00 |
| 2 | 1 | Waterstones | 7.00 |
| 3 | 1 | Amazon | 2.50 |
| 4 | 2 | WHSmith | 4.00 |
| 5 | 2 | Borders | 4.50 |
+----+---------+-------------+-------+
(table b)
If I do a simple select that grabs a book and all of the places it is sold using a join such as:
SELECT
books.*,
shops.shop_name,
shops.price
FROM
books
JOIN shops ON books.pk = shops.book_id
WHERE
book.book_name = "The Lost Symbol"
I would get results such as below:
+----+-----------------+-----------+-------------+-------+
| pk | title | author_id | shop_name | price |
+----+-----------------+-----------+-------------+-------+
| 1 | The Lost Symbol | 1 | WHSmith | 5.00 |
| 1 | The Lost Symbol | 1 | Waterstones | 7.00 |
| 1 | The Lost Symbol | 1 | Amazon | 2.50 |
+----+-----------------+-----------+-------------+-------+
(table c)
However, I would LIKE to receive results like this:
+----+-----------------+-----------+-------------+-------+
| pk | title | author_id | shop_name | price |
+----+-----------------+-----------+-------------+-------+
| 1 | The Lost Symbol | 1 | NULL | NULL |
| 1 | The Lost Symbol | 1 | WHSmith | 5.00 |
| 1 | The Lost Symbol | 1 | Waterstones | 7.00 |
| 1 | The Lost Symbol | 1 | Amazon | 2.50 |
+----+-----------------+-----------+-------------+-------+
(table d)
I.e. the first row is just the result of left outer join and the rest of the results are the the inner join.
An even more desired outcome is:
+------+-----------------+-----------+-------------+-------+
| pk | title | author_id | shop_name | price |
+------+-----------------+-----------+-------------+-------+
| 1 | The Lost Symbol | 1 | NULL | NULL |
| NULL | NULL | NULL | WHSmith | 5.00 |
| NULL | NULL | NULL | Waterstones | 7.00 |
| NULL | NULL | NULL | Amazon | 2.50 |
+------+-----------------+-----------+-------------+-------+
(table e)
Having shop_name and price concatenated and grouped in a single row seems not to work as it only does the first result from shops instead of all of them, also in my real world scenario, I have punctuation in the data so have to be careful with the separator.
So how would I get the result of table e?
You can use UNION ALL to build the required result set:
SELECT pk, title, author_id, NULL AS shop_name, NULL AS price
FROM books
WHERE books.title = "The Lost Symbol"
UNION ALL
SELECT NULL AS pk, NULL AS title, NULL AS author_id, shops.shop_name, shops.price
FROM books
JOIN shops ON books.pk = shops.book_id
WHERE books.title = "The Lost Symbol"
The first part of the union operation returns the first row of the result, i.e. the book title. The second part returns the rest of the rows, i.e.the shop names.
Demo here
I have 2 tables, one containing the main information with a second table containing the same fields plus additional. The second table would have data which should overwrite the first table when queried. The fields are bigger, I've just shortened them down to show here.
First table (products):
+----+--------------+--------------+---------------+--------------------------+
| id | manufacturer | product_name | product_title | product_description |
+----+--------------+--------------+---------------+--------------------------+
| 1 | testingA | productA | productTitleA | main product description |
| 2 | testingA | productB | productTitleB | main product description |
| 3 | testingA | productC | productTitleC | main product description |
+----+--------------+--------------+---------------+--------------------------+
Second Table (products_secondary)
+----+------------+--------------+--------------+---------------+----------------------+---------+
| id | product_id | manufacturer | product_name | product_title | product_description | context |
+----+------------+--------------+--------------+---------------+----------------------+---------+
| 1 | 1 | (null) | (null) | (null) | new description here | test |
+----+------------+--------------+--------------+---------------+----------------------+---------+
My aim is to select the values from the second table where context = text
Expected result:
+----+--------------+--------------+---------------+--------------------------+
| id | manufacturer | product_name | product_title | product_description |
+----+--------------+--------------+---------------+--------------------------+
| 1 | testingA | productA | productTitleA | new description here |
| 2 | testingA | productB | productTitleB | main product description |
| 3 | testingA | productC | productTitleC | main product description |
+----+--------------+--------------+---------------+--------------------------+
The query which I know works is the following:
SELECT IFNULL(`products_secondary`.`product_description` , `products`.`product_description`) AS `product_description`
FROM `products`
LEFT JOIN `products_secondary` ON `products_secondary`.`product_id` = `products`.`id` AND `products_secondary`.`context` = 'test'
I'm sure there is an easier way to do this than having to supply IFNULL() for each field. Here is an SQL Fiddle: http://sqlfiddle.com/#!9/6985b/1
Thanks.
Use this query
SELECT a.id, a.manufacturer, a.product_name, a.product_title, IF(b.context IS NOT NULL, b.product_description, a.product_description) as product_description
FROM
products a
LEFT OUTER JOIN products_secondary b ON a.id=b.product_id
I do have a mysql product table with the columns of id, name, description, and category_id.
This is the result when it select id, name and category_id.
+----+-------------+----------------------------+
| id | category_id | name |
+----+-------------+----------------------------+
| 6 | 1 | category name |
| 7 | 2 | category name |
| 8 | 3 | category name |
| 9 | 2 | category name |
| 11 | 2 | category name |
| 15 | 3 | category name |
| 13 | 4 | category name |
| 14 | 1 | category name |
| 15 | 2 | category name |
| 16 | 2 | category name |
| 17 | 3 | category name |
| 18 | 4 | category name |
| 19 | 1 | category name |
+----+-------------+----------------------------+
My question is, Just I need to select newly added 4 products from above table. These 4 products should be 4 different categories.
This is how I tried it. But its not working for me.
SELECT p.id
, p.category_id
, p.name
, p.description
FROM products p
WHERE p.category_id IN ('1', '2', '3', '4')
ORDER BY added_date DESC
LIMIT 4
+----+-------------+--------+-------------+
| id | category_id | name | description |
+----+-------------+--------+-------------+
| 8 | 4 | dfadff | dfasf |
| 7 | 4 | dffdsf | fdfdfaf |
| 6 | 3 | fdfdsf | fdsfdsfd |
| 5 | 2 | dffdsf | dfsfsf |
+----+-------------+--------+-------------+
4 rows in set (0.00 sec)
Result from above query:
You have to first decide which product is the "last" for each category, that can be simple max(id) if your id is autoincrementing. Then you pick products by these ids:
select p.id, p.category_id, p.name
from products p
join (
select max(id) id from products group by category_id
) tmp using(id);
It can be seen in action at http://sqlfiddle.com/#!9/c86fa/2
If you need to check the WHERE p.category_id IN ('1', '2', '3', '4') part, it is enough to put it inside the subquery.