Query multiple tables including rows with null foreign keys MySQL - mysql

It's my first time with mysql and I need some help. I couldn't find any solution to this probably because it's too easy or I'm too noob to look it up properly yet.
I want to make a query from multiple tables from my test database. Let's say tables are:
category (*id_category, *id_product)
product (*id_product, id_category, id_supplier, id_manufacturer)
manufacturer (*id_manufacturer, name)
supplier (*id_supplier, name)
The columns with * are integer primary key not null with auto_increment(starting from 1). The other id columns are just integer foreign keys(default null).
I need a query also including rows with null id_manufacturer and/or id_supplier on the product table. Those empty fields in the product table return value '0', so when I make a query like
SELECT category.id_product, manufacturer.name, supplier.name
FROM category, product, manufacturer, supplier
WHERE category.id_category = product.id_category AND manufacturer.id_manufacturer = product.id_manufacturer AND supplier.id_supplier = product.id_supplier;
None of the rows with null values(0)are shown.
I'd like you to point me in the most clean and efficient way to do it without changing table properties. Any help would be very much appreciated.

You need to use Left Join then.
SELECT category.id_product, manufacturer.name, supplier.name, P.ID_PRODUCT
FROM product
LEFT JOIN category C
ON C.id_category = P.id_category
LEFT JOIN manufacturer M
ON M.id_manufacturer = P.id_manufacturer
LEFT JOIN supplier S
ON S.id_supplier = P.id_supplier;
These will give all the rows of product table even if it has no corresponding row from other tables.

Related

WHERE clause with INNER JOIN and Sub Query

I have achieved my desired query but I want to know how this one worked. I have multiple tables on my database and my requirements was to take the id from table called product and using this id, I want to retrieve some data from multiple tables and product id is a foreign key to the other tables. The query below works fine (by the way I was just experimenting and luckily got this query).
SELECT ponsfdp.*, product.pName, product.pImage, product.productSizes FROM product
INNER JOIN priceOnSizesForDigitalPrinting AS ponsfdp ON ponsfdp.pId_fk =
(SELECT pId FROM product WHERE pName LIKE "%booklet%")
WHERE pName LIKE "%booklet%";
But when I tried this query,
SELECT ponsfdp.*, product.pName, product.pImage, product.productSizes FROM product
INNER JOIN priceOnSizesForDigitalPrinting AS ponsfdp ON ponsfdp.pId_fk =
(SELECT pId FROM product WHERE pName LIKE "%booklet%");
It contains all the data even with null fields too. Can someone explain to me how it works? My personal opinion is both query should return same data because on the second query, I am using a subquery and it returns only one id, on the other hand, first query has a WHERE clause which generates the same id but by the help of name. How does the first query returns very specific columns and second return all columns even null columns too? I need an explanation for both queries.
Your first query also returning all rows as returned from your second query. But, when you are adding the last filter-
WHERE pName LIKE "%booklet%"
It's just keeping one single row from all rows where pName is like 'booklet'. You can consider the output from your second query as a single table and your logic working as below-
SELECT * FROM (
SELECT ponsfdp.*, product.pName, product.pImage, product.productSizes
FROM product
INNER JOIN priceOnSizesForDigitalPrinting AS ponsfdp
ON ponsfdp.pId_fk = (SELECT pId FROM product WHERE pName LIKE "%booklet%")
)A
WHERE pName LIKE "%booklet%"
Hope this will at least give you some insight of your query.
I don't see any need for a subquery here. You should be using the where condition to select rows from your FROM table, then use the ON clause of your join to find the right record(s) in your joined table for each row of the FROM table:
SELECT ponsfdp.*, product.pName, product.pImage, product.productSizes
FROM product
INNER JOIN priceOnSizesForDigitalPrinting AS ponsfdp
ON ponsfdp.pId_fk = pId
WHERE pName LIKE "%booklet%";

How to use "Count/per" in an sql query

Introduction
I have the following sql tables. Every entity has one-to-many variations and every variation has one-to-many entities.
- ENTITY (ID INTEGER PRIMARY KEY, ID_ENTITY TEXT NOT NULL, wiki_title TEXT);
- VARIATIONS (ID INTEGER PRIMARY KEY NOT NULL, NAME TEXT, LANGUAGE TEXT, TYPE TEXT);
- VAR_ENTITE (ID_ENTITE TEXT NOT NULL, ID_VAR INTEGER NOT NULL, PRIMARY KEY(ID_ENTITE, ID_VAR), FOREIGN KEY(ID_ENTITE) REFERENCES ENTITE(ID_ENTITE), FOREIGN KEY (ID_VAR) REFERENCES VARIATIONS(ID));
question
How can I use sql language in order to count the number of variations per entity ?
List of tables with sample data
Entity
Table entity has 3 records
ID Id_ENTITE wiki_title
48398|m.0nkkw65|Record label|
48399|m.0cp5rvm|Location|
117142|m.0n479hn|Yuika_Sugasawa|
Var_ENTITE
Table Var_entite shows that Entities m.0nkkw65 and m.0760j5n have only 1 variation whereas m.0n479hn has 2 variations.
Id_entity Id_var
m.0nkkw65|109628
m.0760j5n|109631
m.0n479hn|261081
m.0n479hn|261082
Variations
109628|A.G. Producciones|es|Label
109631|Screven County Middle School|en|Label
261081|Yuika Sugasawa|en|Label
261082|菅澤優衣香|ja|Label
Results
As a result I should be able to see the following:
m.0nkkw65: 1
m.0760j5n: 1
m.0n479hn: 2
This will work. Just count the number of variations associated with entity name like 'Angela Merkel'
SELECT COUNT(v.ID) AS Number_OF_Variations
FROM Variation v
INNER JOIN VAR_ENTITE ve ON ve.ID_VAR =v.ID
INNER JOIN ENTITY e ON e.ID=ve.ID_ENTITE
WHERE e.wiki_title = 'Angela Merkel'
Looks like a simple join, with a COUNT. Guessing at how your tables join together:-
SELECT a.id, a.id_entite, COUNT(c.id) AS variation_count
FROM entity a
LEFT OUTER JOIN var_entite b
ON a.id = b.id_entite
LEFT OUTER JOIN variations c
ON b.id_var = c.id
GROUP BY a.id, a.id_entite
Note that you might not need to join to the variations table (can just use the id from the var_entite table if there is nothing else you need from variations). Also if all items have at least one variation you could probably switch to using INNER JOINs rather than LEFT OUTER JOINs

Query with sub-results from another table

I have the following two database tables:
`membership_members` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` tinytext NOT NULL,
`active` tinyint(1) NOT NULL DEFAULT '1',
PRIMARY KEY (`id`)
)
`membership_payments` (
`date` date NOT NULL,
`amount` int(11) NOT NULL,
`member` int(11) NOT NULL,
KEY `member` (`member`)
)
I wish to list all the members, and for each of them I would like to show the list of all the payments by that member. The membership_payments.member column is a foreign key membership_members.id (this is MySQL so I cannot explicitly specify a foreign key). Notice that I do want to list a member even if he doesn't have any payments.
So something like:
* John Smith
- 2012-05-06 $100
- 2012-01-02 $100
* Brian Jones
* Mike Jackson
- 2012-09-02 $50
I have tried with this query:
SELECT id, name, active, date, amount
FROM `membership_members`,
`membership_payments`
WHERE membership_members.id = member
This of course gives me tha data I need, but not exactly how I need it as it returns a row for each payment. That means that I later have to group the rows (in PHP) by member id so that I don't list a member multiple times. I can do that, but I believe that it would be easier and faster to do it in SQL. Also, this query only gives me users which have payments with their id.
I feel that this should be simple, but last time I did anything but the most simple stuff in SQL was 6-7 years ago.
EDIT:
LEFT OUTER JOIN suggested in one of the answers solves the issue with the "missing" members. But what is the best way of grouping results by member IDs? I know there is a GROUP BY clause, but it doesn't give me all the payments for the given member, only one. I suppose I can run a new payments query for each member, but I fear this would be very inefficient (we have around 300-400 members).
You'll need to JOIN the two tables based on referencing key columns.
SELECT id, name, active, date, amount
FROM membership_members
LEFT OUTER JOIN membership_payments
ON membership_members.id = membership_payments.member;
I chose LEFT OUTER JOIN so that the members without payments are also shown.
For more info on joins check http://dev.mysql.com/doc/refman/5.0/en/join.html
EDIT
Use ORDER BY membership_members.id to get records ordered by a certain column.
Grouping does not behave like sorting. GROUPING merges all records by the column you provided. ORDER BY sorts
use LEFT JOIN
basically, what you are doing now is an INNER JOIN. INNER JOIN only displays a records if it has atleast one record on each table. But LEFT JOIN operates differently. It displays all records on the Lefthand side table whether it has matching record or not on the righthand side table.
SELECT id, name, active, date, amount
FROM `membership_members` a
LEFT JOIN `membership_payments` b
ON a.ID = b.member
To further gain more knowledge about joins, kindly visit the link below:
Visual Representation of SQL Joins
Take a look at GROUP_CONCAT MySQL clause.
Example
OR
You can use CONCAT.
Example
SELECT CONCAT(`membership_payments.date`, ' ', `membership_payments.amount`)
FROM `membership_members`
LEFT OUTER JOIN membership_payments
ON membership_members.id = membership_payments.member;

MySQL DELETE with JOIN on condition

I have 3 tables; products, variants, and stock {simplified}
PRODUCTS
id
name
discontinued (ENUM: 0 or 1)
etc
VARIANTS
id
product_id
colour
size
STOCK
id
variant_id
branch_id
When the user selects to discontinue the PRODUCT I set the discontinued flag to 1. That's fine, but I want to delete the PRODUCT and VARIANTS records altogether if there is no STOCK record of the product. Obviously I can do this using a SELECT query first in PHP but I would like to do it in one mySQL query. This is what I have so far, but it is returning an error from mySQL:
$query = "DELETE FROM prod_lines,
JOIN variants ON variants.lineid = prod_lines.id
WHERE prod_lines.id = '$lineid' AND
(SELECT COUNT(id) FROM stock WHERE stock.variantid = variants.id) = 0";
Can anybody out there help me come up with the right solution? Maybe there is a better way that doesn't even involve a subquery?
Thanks in advance
I assume that the product_line table in your query is the PRODUCTS table you describe in the beginning of your post.
DELETE prod_lines.* -- you must specify which table you are deleting from, because there are several tables in the FROM clause
FROM prod_lines
JOIN variants ON variants.lineid = prod_lines.id
LEFT JOIN stock ON stock.variantid = variants.id
WHERE prod_lines.id = #lineid -- your "$lineid" variable here
AND stock.id IS NULL; -- selects only items with no match in the "stock" table
http://sqlfiddle.com/#!2/40a21
Try something like this,the downside is though you have to have two php variables here and also an nested query that you may not be ok with
DELETE FROM (select COUNT(id) countstock FROM stock s,variants v WHERE s.variantid = v.id and v.productid='$lineid') prod
where prod.countstock>1 and prod.productid='$lineid'

MySQL - Create new table using keys from three existing tables

How do I use the information from existing MySQL tables to populate a new table. I have three tables each of which have a key that I want to include in the new table like so:
TRANSACTIONS TABLE
tr_id INT NOT NULL AUTO_INCREMENT
other columns
.
.
PEOPLE TABLE
p_id INT NOT NULL AUTO_INCREMENT
tr_id INT
other columns
.
.
HOLIDAYS TABLE
h_id INT NOT NULL AUTO_INCREMENT
tr_id INT
other columns
.
.
TRACKING TABLE
track_id INT NOT NULL AUTO_INCREMENT
tr_id
p_id
h_id
other columns
.
.
I'm not even sure that I need the p_id and h_id columns in the new table, because I just need to be able to get all the rows from the tracking table for a single tr_id, but they may come in useful, so I might leave them in there. (I do need them to create the rows in the new table because one transaction can relate to multiple people going on multiple holiday destinations and each row in the tracking table ust relate to a single destination for a single person, so for 2 people each going to the same 2 holiday destinations, there will be 4 rows in the tracking table)
I have tried:
INSERT INTO tracking (tr_id, p_id, h_id) VALUES
(SELECT t.tr_id, p.p_id, h.h_id
FROM transactions t
JOIN people p
JOIN holidays h
WHERE t.tr_id = p.tr_id
AND t.tr_id = h.tr_id);
but this is giving me an error.
Grateful for any advice on this. Many thanks.
You can skip the Values infront of select,
and you could use the keyword "USING" if you want to
connect the tables on a column that have the same name in both tables.
i should have used:
INSERT INTO tracking (tr_id, p_id, h_id)
SELECT tr_id, p_id, h_id
FROM transactions
LEFT JOIN people USING (tr_id)
LEFT JOIN holidays USING (tr_id)
There is a syntax error. When you use Join, it is followed by the ON keyword which specifies which columns need to match. The following query will work:
INSERT INTO tracking (tr_id, p_id, h_id) VALUES
(SELECT t.tr_id, p.p_id, h.h_id
FROM transactions t
JOIN people p
on t.tr_id = p.tr_id
JOIN holidays h
on t.tr_id = h.tr_id)
You can read more about JOINS over here
UPDATE
This should work:
INSERT INTO tracking (tr_id, p_id, h_id)
SELECT t.tr_id, p.p_id, h.h_id
FROM transactions t
JOIN people p
on t.tr_id = p.tr_id
JOIN holidays h
on t.tr_id = h.tr_id
Reference: MySQL Insert & Joins
The reference that I have provided does not use the VALUES keyword in the query. Strange syntax, but let me know if that works.