mysql find missing items - mysql

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
;

Related

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.

Join by part of string

I have following tables:
**visitors**
+---------------------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+---------------------+--------------+------+-----+---------+----------------+
| visitors_id | int(11) | NO | PRI | NULL | auto_increment |
| visitors_path | varchar(255) | NO | | | |
+---------------------+--------------+------+-----+---------+----------------+
**fedora_info**
+----------------+--------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+----------------+--------------+------+-----+---------+-------+
| pid | varchar(255) | NO | PRI | | |
| owner_uid | int(11) | YES | | NULL | |
+----------------+--------------+------+-----+---------+-------+
First I looking for visitors_path that are related to specific pages by:
SELECT visitors_id, visitors_path
FROM visitors
WHERE visitors_path REGEXP '[[:<:]]fedora/repository/.*:[0-9]+$';
The above query return expected result.
now .*:[0-9]+ in above query referred to pid in second table. now I want know count of result in above query grouped by owner_uid in second table.
How can I JOIN this tables?
EDIT
sample data:
visitors
+-------------+---------------------------------+
| visitors_id | visitors_path |
+-------------+---------------------------------+
| 4574 | fedora/repository/islandora:123 |
| 4575 | fedora/repository/islandora:123 |
| 4580 | fedora/repository/islandora:321 |
| 4681 | fedora/repository/islandora:321 |
| 4682 | fedora/repository/islandora:321 |
| 4704 | fedora/repository/islandora:321 |
| 4706 | fedora/repository/islandora:456 |
| 4741 | fedora/repository/islandora:456 |
| 4743 | fedora/repository/islandora:789 |
| 4769 | fedora/repository/islandora:789 |
+-------------+---------------------------------+
fedora_info
+-----------------+-----------+
| pid | owner_uid |
+-----------------+-----------+
| islandora:123 | 1 |
| islandora:321 | 2 |
| islandora:456 | 3 |
| islandora:789 | 4 |
+-----------------+-----------+
Expected result:
+-----------------+-----------+
| count | owner_uid |
+-----------------+-----------+
| 2 | 1 |
| 4 | 2 |
| 3 | 3 |
| 2 | 4 |
| 0 | 5 |
+-----------------+-----------+
I suggest you to normalize your database. When inserting rows in visitors extract pid in the front end language and put it in a separate column (e.g. fi_pid). Then you can join it easily.
The following query might work for you. But it'll be little cpu intensive.
SELECT
COUNT(a.visitors_id) as `count`,
f.owner_uid
FROM (SELECT visitors_id,
visitors_path,
SUBSTRING(visitors_path, ( LENGTH(visitors_path) -
LOCATE('/', REVERSE(visitors_path)) )
+ 2) AS
pid
FROM visitors
WHERE visitors_path REGEXP '[[:<:]]fedora/repository/.*:[0-9]+$') AS `a`
JOIN fedora_info AS f
ON ( a.pid = f.pid )
GROUP BY f.owner_uid
Following query returns expected result, but its very slow Query took 9.6700 sec
SELECT COUNT(t2.pid), t1.owner_uid
FROM fedora_info t1
JOIN (SELECT TRIM(LEADING 'fedora/repository/' FROM visitors_path) as pid
FROM visitors
WHERE visitors_path REGEXP '[[:<:]]fedora/repository/.*:[0-9]+$') t2 ON t1.pid = t2.pid
GROUP BY t1.owner_uid

MYSQL outer join, doesn't return non-matching rows

I have these two tables in my db
describe external_review_sources;
+-------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-------+--------------+------+-----+---------+----------------+
| ersID | int(11) | NO | PRI | NULL | auto_increment |
| name | varchar(50) | NO | UNI | NULL | |
| logo | varchar(255) | NO | | NULL | |
+-------+--------------+------+-----+---------+----------------+
3 rows in set (0.00 sec)
And
describe listing_external_review_source_rel;
+---------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+---------+--------------+------+-----+---------+----------------+
| lersrID | int(11) | NO | PRI | NULL | auto_increment |
| bid | int(10) | NO | | NULL | |
| url | varchar(255) | NO | | NULL | |
| ersID | int(11) | YES | | NULL | |
| active | int(10) | NO | | NULL | |
| order | int(10) | NO | | NULL | |
+---------+--------------+------+-----+---------+----------------+
6 rows in set (0.00 sec)
I query these tables this way:
SELECT *
FROM
listing_external_review_source_rel
RIGHT JOIN
external_review_sources USING(ersID)
where bid=902028 or bid IS NULL;
+-------+---------------+------+---------+--------+-------+--------+-------+
| ersID | name | logo | lersrID | bid | url | active | order |
+-------+---------------+------+---------+--------+-------+--------+-------+
| 1 | G1 | a | 17 | 902028 | url11 | 1 | 0 |
| 2 | D1 | b | 18 | 902028 | url22 | 0 | 0 |
+-------+---------------+------+---------+--------+-------+--------+-------+
2 rows in set (0.00 sec)
As you can see results are showing up for bid=902028, how ever for a bid such as 866696 that does NOT exist in listing_external_review_source_rel, the results are empty
SELECT *
FROM listing_external_review_source_rel
RIGHT JOIN external_review_sources USING(ersID)
where bid=866696 or bid IS NULL;
Empty set (0.00 sec)
I expect the results to be this:
+-------+---------------+------+---------+--------+-------+--------+-------+
| ersID | name | logo | lersrID | bid | url | active | order |
+-------+---------------+------+---------+--------+-------+--------+-------+
| 1 | G1 | NULL | NULL| NULL | NULL | NULL | NULL |
| 2 | D1 | NULL | NULL| NULL | NULL | NULL | NULL |
+-------+---------------+------+---------+--------+-------+--------+-------+
2 rows in set (0.00 sec)
That's what I have used the "or bid IS NULL" condition.
What am I doing wrong and what query would give me this result? I basically am interested in having nonmatching rows in my results as well.
Most people use LEFT JOIN so I'll rewrite it to be more standard:
SELECT *
FROM external_review_sources a LEFT JOIN
listing_external_review_source_rel b ON a.ersID=b.ersID AND bid=866696;
Remember that an outter join returns all rows where the ON condition matches, and NULL where they don't. In this case your match condition is more than just the ersID
Try this
SELECT * FROM
external_review_sources e
LEFT JOIN
listing_external_review_source_rel r ON e.ersID = r.ersID AND r.bid = 866696
WHERE
r.bid IS NULL;
When filtering on "outer tables", the filter needs to be a derived table or in the JOIN because you want to filter before the WHERE (logically). Also, a best practice is to use LEFT JOIN for clarity.
With a derived table
SELECT * FROM
external_review_sources e
LEFT JOIN
(
SELECT *
FROM listing_external_review_source_rel
WHERE bid = 866696
) r USING (ersID)
WHERE
r.bid IS NULL;
Try it this way, moving the test of the bid column out of the WHERE clause and into the JOIN:
SELECT *
FROM listing_external_review_source_rel ler
RIGHT JOIN external_review_sources ers
ON ler.ersID = ers.ersID
AND ler.bid=866696;

MySQL query using INNER JOIN, Not returning what i'd expect

I'm having trouble with the following query:
SELECT costingjobtimes.TimeStamp, costingdepartment.DeptDesc, costingemployee.Name, costingjobtimes.TimeElapsed FROM costingemployee INNER JOIN (costingdepartment INNER JOIN costingjobtimes ON costingdepartment.DeptID = costingjobtimes.DeptID) ON costingemployee.EmployeeID = costingjobtimes.EmployeeID;
I would expect it to return every row in the costingjobtimes database however it's only returning 4 at current.
Basically, I have 3 tables. (costingjobtimes, costingdepartment, costingemployee) They are as follows:
mysql> DESCRIBE costingemployee
-> ;
+------------+------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+------------+------------+------+-----+---------+-------+
| EmployeeID | varchar(8) | | PRI | | |
| Name | text | | | | |
+------------+------------+------+-----+---------+-------+
2 rows in set (0.00 sec)
mysql> DESCRIBE costingdepartment
-> ;
+----------+------------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+----------+------------------+------+-----+---------+-------+
| DeptID | int(10) unsigned | | PRI | 0 | |
| DeptDesc | text | | | | |
+----------+------------------+------+-----+---------+-------+
2 rows in set (0.00 sec)
mysql> DESCRIBE costingjobtimes
-> ;
+-------------+------------------+------+-----+---------------------+-----------
-----+
| Field | Type | Null | Key | Default | Extra
|
+-------------+------------------+------+-----+---------------------+-----------
-----+
| id | int(10) unsigned | | PRI | NULL | auto_incre
ment |
| EmployeeID | text | | | |
|
| DeptID | int(10) unsigned | | | 0 |
|
| JobNumber | text | | | |
|
| TimeElapsed | decimal(10,5) | | | 0.00000 |
|
| TimeStamp | datetime | | | 0000-00-00 00:00:00 |
|
| JobRef | text | | | |
|
+-------------+------------------+------+-----+---------------------+-----------
-----+
7 rows in set (0.00 sec)
So all the query is supposed to do is return all the rows from costingjobtimes, but put in the employees name instead of EmployeeID and Department description instead of the DeptID. Any help would be great..
Thanks in advance,
I don't know if an Inner Join is really what you are looking for teishu. Maybe you should try a normal join.
For example:
select e.Name, d.DeptDesc, j.JobNumber, j.TimeElapsed, j.TimeStamp, j.JobRef
from costingjobtimes as j
join costingdepartment as d on d.DeptID = j.DeptId
join costingemployee as e on e.EmployeeID = j.EmployeeID
This should give you a resultset with the information from the costingjobtimes table combined with the name of the employee and the description of the department.
Hope that helpes.