mysql natural join not working - mysql

I have two tables in mysql server. I use these tables for studing JOIN multiple tables but something appears to be incorrect:
mysql> select * from category;
+-------------+-----------+
| category_id | name |
+-------------+-----------+
| 1 | fruit |
| 2 | vegetable |
+-------------+-----------+
2 rows in set (0.00 sec)
mysql> desc category;
+-------------+-------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-------------+-------------+------+-----+---------+----------------+
| category_id | int(11) | NO | PRI | NULL | auto_increment |
| name | varchar(50) | NO | | NULL | |
+-------------+-------------+------+-----+---------+----------------+
2 rows in set (0.00 sec)
And:
mysql> select * from goods;
+---------+--------+-------------+------+
| good_id | name | category_id | cost |
+---------+--------+-------------+------+
| 1 | banan | 1 | 1.00 |
| 2 | potato | 2 | 1.00 |
| 3 | peach | 1 | 1.00 |
+---------+--------+-------------+------+
3 rows in set (0.00 sec)
mysql> desc goods;
+-------------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-------------+--------------+------+-----+---------+----------------+
| good_id | int(11) | NO | PRI | NULL | auto_increment |
| name | varchar(100) | NO | | NULL | |
| category_id | int(11) | NO | MUL | NULL | |
| cost | decimal(6,2) | NO | | NULL | |
+-------------+--------------+------+-----+---------+----------------+
4 rows in set (0.00 sec)
The second table has foreign key (category_id) and I can join them using INNER JOIN:
mysql> select c.name category, g.name, g.cost from category as c INNER JOIN goods g ON c.category_id = g.category_id;
+-----------+--------+------+
| category | name | cost |
+-----------+--------+------+
| fruit | banan | 1.00 |
| vegetable | potato | 1.00 |
| fruit | peach | 1.00 |
+-----------+--------+------+
3 rows in set (0.00 sec)
I tried to use NATURAL JOIN but it didnt work and it seems I dont know why(((
mysql> select c.name, g.name, g.cost from category as c NATURAL JOIN goods g;
Empty set (0.00 sec)
Could somebody explain why NATURAL JOIN does not work?

I was having the exact same thing happen to me, and my Googling led me to this question. I eventually figured it out, so I figured I'd post my answer here.
This was the culprit:
Instead of specifying a join condition through ON, USING or a WHERE clause, the NATURAL keyword tells the server to match up any column names between the two tables, and automatically use those columns to resolve the join.
Your fruit and category tables both have a column called "name". When SQL tries to join the two, it tries to join all like columns. So thus, category_id==category_id, but name!=name.
Rename your columns tablename_column instead.

Related

MYSQL filled some rows, but was't suppost to

So I made a basic struct of a database, and mysql filled some rows on a table and I don't know why.
Maybe I can explain myself with the tables
mysql> show fields
-> from products;
+--------------------+--------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+--------------------+--------------+------+-----+---------+-------+
| ID_PRODUCT | decimal(8,0) | NO | PRI | NULL | |
| ID_CATEGORY | decimal(8,0) | NO | MUL | NULL | |
| NAME | text | NO | | NULL | |
| BRAND | text | NO | | NULL | |
| PICTURE | longblob | YES | | NULL | |
| OBSERVATIONS | text | YES | | NULL | |
| QUANT_UNIT | text | NO | | NULL | |
+--------------------+--------------+------+-----+---------+-------+
8 rows in set (0.00 sec)
So this is a table with products, and I have one other table with the prices so I can save the history of prices
mysql> show fields from prices;
+------------+--------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+------------+--------------+------+-----+---------+-------+
| ID_PRODUCT | decimal(8,0) | NO | MUL | NULL | |
| PRICE | text | NO | | NULL | |
| TIME | timestamp | NO | | NULL | |
+------------+--------------+------+-----+---------+-------+
3 rows in set (0.00 sec)
And if I insert the values into the product and prices tables
mysql> insert into products (id_product, id_category, name, brand, quant_unit)
-> values (1, 0, 'rice', 'XPTO', 'kg');
Query OK, 1 row affected (0.01 sec)
mysql> select * from products;
+------------+--------------+--------+----------+----------------+-------------+--------------------+
| ID_PRODUCT | ID_CATEGORY | NAME | BRAND | PICTURE | OBSERVATION | QUANT_UNIT |
+------------+--------------+--------+----------+----------------+-------------+--------------------+
| 0 | 0 | mug | no_brand | 0x | favorite | un |
| 1 | 0 | rice | xpto | 0x | NULL | kg |
+------------+--------------+--------+----------+----------------+-------------+--------------------+
2 rows in set (0.00 sec)
mysql> insert into prices (id_product, price, time)
-> values (0, '5', CURRENT_TIMESTAMP);
Query OK, 1 row affected (0.01 sec)
mysql> select * from prices;
+------------+-------+---------------------+
| ID_PRODUCT | PRICE | TIME |
+------------+-------+---------------------+
| 0 | 7.30 | 2020-11-27 23:25:34 |
| 1 | 5 | 2020-11-27 23:29:12 |
+------------+-------+---------------------+
2 rows in set (0.00 sec)
all fine so far, but if I do a select with both of the tables
mysql> select pro.id_product, pro.id_category, pro.name, pro.brand, pro.quant_unit, pre.price
-> from products pro, prices pri;
+------------+--------------+--------+----------+-------------+-------+
| id_product | id_category | name | brand | quant_unit | price |
+------------+--------------+--------+----------+-------------+-------+
| 0 | 0 | mug | no_brand | un | 7.30 |
| 1 | 0 | rice | xpto | kg | 7.30 |
| 0 | 0 | mug | no_brand | un | 5 |
| 1 | 0 | rice | xpto | kg | 5 |
+------------+--------------+--------+----------+-------------+-------+
4 rows in set (0.00 sec)
those are my constraints to connect the tables
alter table PRICES add constraint FK_PRICES_COST_PRODUCTS foreign key (ID_PRODUCT)
references PRODUCTS (ID_PRODUCT) on delete restrict on update restrict;
alter table PRODUCTS add constraint FK_PRODUCTS_PERTENCE_CATEGORY foreign key (ID_CATEGORY)
references CATEGORY (ID_CATEGORY) on delete restrict on update restrict;
I can't even search the problem because it doesn't make sense to me... But I'm new at SQL, so I think I'm doing something wrong...
I will appreciate any feedback, thanks!
I advise against using comma-join but in your query you didn't specify what to match between those two tables. You can simply fix that by adding WHERE in your query like this:
select pro.id_product, pro.id_category, pro.name, pro.brand, pro.quant_unit, pre.price
from products pro, prices pri
where pro.id_product=pri.id_product;
But nowadays, most people uses JOIN instead of comma-join, hence:
select pro.id_product, pro.id_category, pro.name, pro.brand, pro.quant_unit, pre.price
from products pro JOIN prices pri
ON pro.id_product=pri.id_product;
There's a lot of JOIN types in MySQL and you can refer to this documentation.

Sql query performance is varying though they are the same

There are 2 tables and their structure as below:
mysql> desc product;
+-------+-------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------+-------------+------+-----+---------+-------+
| id | int(11) | NO | PRI | NULL | |
| brand | varchar(20) | YES | | NULL | |
+-------+-------------+------+-----+---------+-------+
2 rows in set (0.02 sec)
mysql> desc sales;
+-------------+-------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------------+-------------+------+-----+---------+-------+
| id | int(11) | YES | | NULL | |
| yearofsales | varchar(10) | YES | | NULL | |
| price | int(11) | YES | | NULL | |
+-------------+-------------+------+-----+---------+-------+
3 rows in set (0.01 sec)
Here id is the foreign key.
And Queries are as follows:
1.
mysql> select brand,sum(price),yearofsales
from product p, sales s
where p.id=s.id
group by s.id,yearofsales;
+-------+------------+-------------+
| brand | sum(price) | yearofsales |
+-------+------------+-------------+
| Nike | 917504000 | 2012 |
| FF | 328990720 | 2010 |
| FF | 328990720 | 2011 |
| FF | 723517440 | 2012 |
+-------+------------+-------------+
4 rows in set (1.91 sec)
2.
mysql> select brand,tmp.yearofsales,tmp.sum
from product p
join (
select id,yearofsales,sum(price) as sum
from sales
group by yearofsales,id
) tmp on p.id=tmp.id ;
+-------+-------------+-----------+
| brand | yearofsales | sum |
+-------+-------------+-----------+
| Nike | 2012 | 917504000 |
| FF | 2011 | 328990720 |
| FF | 2012 | 723517440 |
| FF | 2010 | 328990720 |
+-------+-------------+-----------+
4 rows in set (1.59 sec)
Question is: Why the second query takes less time than the first one? I have executed it multiple times in different order as well.
You can check the execution plan for the two queries and the indexes on the two tables to see why one query takes more than the other. Also, you cannot run one simple test and trust the results, there are many factors that can impact the execution of queries, like the server being busy with something else when executing one query, so it runs slower. You'll have to run both queries a big number of times and then compare the averages.
However, it is highly recommended to use explicit joins instead of implicit joins:
SELECT brand, SUM(price), yearofsales
FROM product p
INNER JOIN sales s ON p.id = s.id
GROUP BY s.id, yearofsales;

Mysql: How to create a column which is the difference between a column in a Table & another column in a View

In the database 'college2' there are 3 TABLES:'student, course & enrolment', and one(1) VIEW:'enrolment_status', which is created using the following command:
CREATE VIEW enrolment_status AS
SELECT code, COUNT(id)
FROM enrolment
GROUP BY code;
Explain command for 'course,enrolment and enrolment_status' results in:
mysql> EXPLAIN course;
+---------------+-------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+---------------+-------------+------+-----+---------+-------+
| code | char(8) | NO | PRI | NULL | |
| name | varchar(90) | YES | MUL | NULL | |
| max_enrolment | char(2) | YES | | NULL | |
+---------------+-------------+------+-----+---------+-------+
3 rows in set (0.09 sec)
mysql> explain enrolment;
+-------+---------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------+---------+------+-----+---------+-------+
| id | char(6) | YES | MUL | NULL | |
| code | char(8) | YES | MUL | NULL | |
+-------+---------+------+-----+---------+-------+
2 rows in set (0.02 sec)
mysql> explain enrolment_status;
+-----------+------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-----------+------------+------+-----+---------+-------+
| code | char(8) | YES | | NULL | |
| COUNT(id) | bigint(21) | NO | | 0 | |
+-----------+------------+------+-----+---------+-------+
2 rows in set (0.18 sec)
'max_enrolment' column in 'course' TABLE is the maximum allowed # of student for each course, say 10 or 20.
'count(id)' column in 'enrolment_status' VIEW (not table) is actual # of students enrolled in each course.
'id' column in 'enrolment' TABLE is the student id enrolled in a course.
HERE'S MY QUESTION:
I want to have the '# of seats left' which is the difference between 'max_enrolment' column and 'count(id)' column.
'#of seats left' can be a stand alone table or view or a column added to any of the above tables. How can i do this:
I tried many commands including the following,
CREATE VIEW seats_left AS (
SELECT course.code, course.max_enrolment - enrolment_status.count
FROM course, enrolment_status
WHERE course.code = enrolment_status.code);
...which gives me the following error message:
ERROR 1054 (42S22): Unknown column 'enrolment_status.count' in 'field list'
mysql> SELECT*FROM enrolment_status;
+----------+-----------+
| code | COUNT(id) |
+----------+-----------+
| COMP9583 | 7 |
| COMP9585 | 9 |
| COMP9586 | 7 |
| COMP9653 | 7 |
| COMP9654 | 7 |
| COMP9655 | 8 |
| COMP9658 | 7 |
+----------+-----------+
7 rows in set (0.00 sec)
mysql> SELECT code, max_enrolment FROM course;
+----------+---------------+
| code | max_enrolment |
+----------+---------------+
| COMP9583 | 10 |
| COMP9585 | 15 |
| COMP9586 | 15 |
| COMP9653 | 12 |
| COMP9654 | 10 |
| COMP9655 | 12 |
| COMP9658 | 12 |
+----------+---------------+
7 rows in set (0.00 sec)
+----------+---------------------+
| code | max_enrolment - cnt |
+----------+---------------------+
| COMP9583 | 9 |
| COMP9585 | 14 |
| COMP9586 | 14 |
| COMP9653 | 11 |
| COMP9654 | 9 |
| COMP9655 | 11 |
| COMP9658 | 11 |
+----------+---------------------+
7 rows in set (0.09 sec)
Try to use an acronym for in the view.
CREATE VIEW enrolment_status AS
SELECT code, COUNT(id) count
FROM enrolment
GROUP BY code;
Then you should be able to do this:
CREATE VIEW seats_left AS (
SELECT course.code, course.max_enrolment - enrolment_status.count
FROM course, enrolment_status
WHERE course.code = enrolment_status.code);
If you cannot change the view, then you must use the exact same name in the query:
CREATE VIEW seats_left AS (
SELECT course.code, course.max_enrolment - enrolment_status.'count(id)'
FROM course, enrolment_status
WHERE course.code = enrolment_status.code);
Try this:
SELECT b.`code`,max_enrolment - cnt from
(select `code`, cnt from
(select count(1) as cnt,`code` from enrolment_status
GROUP BY `code`) as a) as a
LEFT JOIN
(SELECT code,max_enrolment from course) as b
on a.`code` = b.`code`
You can change left join to right join

mysql INNER_JOIN subquery

I have inherited a MySQL database, that has a table as follows:
mysql> describe stock_groups;
+--------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+--------+--------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| group | varchar(5) | YES | | NULL | |
| name | varchar(255) | YES | | NULL | |
| parent | varchar(5) | YES | | NULL | |
| order | int(11) | YES | | NULL | |
+--------+--------------+------+-----+---------+----------------+
5 rows in set (0.00 sec)
When I run mysql> select * from stock_groups wheregroup='D2';
I get:
mysql> select * from stock_groups where `group`='D2';
+----+-------+------+--------+-------+
| id | group | name | parent | order |
+----+-------+------+--------+-------+
| 79 | D2 | MENS | D | 51 |
+----+-------+------+--------+-------+
1 row in set (0.00 sec)
and also i have a table:
mysql> describe stock_groups_styles_map;
+-------+-------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-------+-------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| group | varchar(5) | NO | | NULL | |
| style | varchar(25) | NO | | NULL | |
+-------+-------------+------+-----+---------+----------------+
3 rows in set (0.01 sec)
when I run:
mysql> select `group` from stock_groups_styles_map where style='N26';
+-------+
| group |
+-------+
| B1 |
| B11 |
| D2 |
| V2 |
+-------+
4 rows in set (0.00 sec)
how do i get the stock_groups.name ?
Join the tables, and select only the data you need. If you need unique rows, use the distinct keyword:
select -- If you need unique names, use "select distinct" instead of "select"
sg.name
from
stock_groups_styles_map as sgs
inner join stock_groups as sg on sgs.group = sg.group
where
sgs.style = 'N26'
You could also solve this using subqueries, but that would be rather inneficient in this case.
Something important
You should add the appropriate indexes to your tables. It will improve the performance of your database.
You can use inner join on group column
SELECT ss.group, sg.name from
stock_groups sg inner join stock_groups_styles_map ss on ss.group = sg.group
where ss.style='N26'
SELECT stock_groups.name FROM stock_groups_styles_map, stock_groups WHERE stock_groups_styles_map.style ='N26';
worked for me.

mysql find missing items

i have a table with the following structure
mysql> describe stock_prices;
+---------------------+-------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+---------------------+-------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| code | varchar(16) | YES | MUL | NULL | |
| pricelist | varchar(10) | YES | MUL | NULL | |
| settlement_discount | tinyint(1) | YES | | NULL | |
| overal_discount | tinyint(1) | YES | | NULL | |
| sale | tinyint(1) | YES | | NULL | |
| price_blob | longtext | YES | | NULL | |
+---------------------+-------------+------+-----+---------+----------------+
7 rows in set (0.00 sec)
when i run this query
mysql> SELECT pricelist, count(pricelist) as dup from stock_prices group by pricelist having dup>1 order by dup;
+-----------+------+
| pricelist | dup |
+-----------+------+
| GMBH | 1843 |
| DISTCART | 2241 |
| DISTSTD | 2241 |
| CART | 2242 |
| USSD | 2242 |
| SPCA | 2242 |
| SPCB | 2242 |
| SPCC | 2242 |
| EUCN | 2242 |
| STD | 2242 |
| EUSD | 2242 |
| USCN | 2242 |
+-----------+------+
12 rows in set (0.03 sec)
all the pricelist items should have the same values, but GMBH has 399 less and DISTCART and DISTSTD have 1 less.
basically, i have code that does not have a pricelist entry.
when i run:
mysql> SELECT code, count(code) as dup from stock_prices group by code having dup>1 order by dup;
+-------------+-----+
| code | dup |
+-------------+-----+
| XN44-CH2 | 9 |
| XN23-MGY1 | 11 |
| XN24-CH2 | 11 |
| XN25-VWH1 | 11 |
| XN36-BL2 | 11 |
| XN36-CH3 | 11 |
| XN37-BL3 | 11 |
| XN38-BC3 | 11 |
| XN38-CE3 | 11 |
....
so in this case XN44-CH2 is missing 3 codes and XN23-MGY1 is missing 1 code
mysql> SELECT COUNT(pricelist) FROM stock_prices WHERE pricelist = 'GMBH';
+------------------+
| COUNT(pricelist) |
+------------------+
| 1843 |
+------------------+
1 row in set (0.00 sec)
what would be the correct way to find out what the missing pricelists for each is?
any advice much appreciated.
Assuming there is a reference table for all the price lists and one for all the codes, you could do something like this in standard SQL:
SELECT
p.pricelist,
c.code
FROM
pricelists AS p
CROSS JOIN
codes AS c
EXCEPT
SELECT
pricelist,
code
FROM
stock_prices
;
That is, get all the combinations of the existing pricelists and codes and subtract those that are present in stock_prices. The result would be the missing pairs.
As MySQL doesn't support EXCEPT, you could implement the same logic with a LEFT JOIN:
SELECT
p.pricelist,
c.code
FROM
pricelists AS p
CROSS JOIN
codes AS c
LEFT JOIN
stock_prices AS s ON p.pricelist = s.pricelist
AND c.code = s.code
WHERE s.id IS NULL
;
If you do not have those reference tables, you could replace them with derived tables in this way:
pricelists ==> (SELECT DISTINCT pricelist FROM stock_prices)
codes ==> (SELECT DISTINCT code FROM stock_prices)
And the query would then look like this:
SELECT
p.pricelist,
c.code
FROM
(SELECT DISTINCT pricelist FROM stock_prices) AS p
CROSS JOIN
(SELECT DISTINCT code FROM stock_prices) AS c
LEFT JOIN
stock_prices AS s ON p.pricelist = s.pricelist
AND c.code = s.code
WHERE s.id IS NULL
;