Let's say I want to join on 2 tables, one is filled like this.
| Category |
|-----------|
| id | name |
|----|------|
| 1 | Foo |
| 2 | Bar |
| 3 | Baz |
The other like this:
| Page |
|-----------|
| id | cat |
|----|------|
| 1 | 0 |
| 2 | 1 |
| 3 | 3 |
As you can see cat 0 in the Page table is not present in the Category table. Our system is unfortunately like this and I can't add the category with id 0 to the Category table, due to other code.
Now comes the one million dollar question: Is it possible to join category.id on page.cat and set an if statement when page.cat equals 0 to show the category name as Default?
If only 0 is the one missing do a left join and use COALESCE to decode the nulls.
SELECT Page.*, COALESCE(name , 'Default')
FROM Page
LEFT JOIN Category
ON Page.cat = Category.id;
or
SELECT P.*, IFNULL(C.name , 'Default') as Name
FROM Page P
LEFT JOIN Category C ON P.cat= C.id
Try this out:
SELECT
p.id,
p.cat,
case when p.cat = 0 then "DEFAULT" ELSE c.name END AS cat_name
FROM
cat c RIGHT JOIN page p
ON c.id = p.cat
ORDER BY p.id
Related
I would like to count the number of rows or in this case the number of products which are in the same category and return zero if there are no products in the category.
The tables in my query looking like this:
category category_lang media
------------ ------------- ---------
category_id | published category_id | name | alias media_id | category_id
----------------------- -------------------------- ----------------------
1 | 1 1 | One | one 1 | 1
2 | 1 2 | Two | two 2 | 2
3 | 1 3 | Three| three 3 | 3
media_lang product_category
------------------------------- ----------------
media_id | url | file_name product_id | category_id
------------------------------- ------------------------
1 | /images/ | file1.jpg 1 | 1
2 | /images/ | file2.jpg 2 | 1
3 | /images/ | file3.jpg 3 | 2
and I would like a result like this:
category_id | category_name | alias | media_id | url | file_name | count
1 | One | one | 1 | /images/ | file1.jpg | 2
2 | Two | two | 2 | /images/ | file2.jpg | 1
3 | Three | three | 3 | /images/ | file3.jpg | 0
My query currently looks like this
SELECT
c.`category_id`,
ca.`name`,
ca.`alias`,
m.`media_id`,
ma.`url`,
ma.`file_name`,
COUNT(p.`product_id`) AS `count`
FROM `category` c
LEFT JOIN `category_lang` ca ON (c.`category_id` = ca.`category_id`)
LEFT JOIN `media` m ON (c.`category_id` = m.`category_id`)
LEFT JOIN `media_lang` ma ON (m.`media_id` = ma.`media_id`)
LEFT JOIN `product_category` p ON (c.`category_id` = p.`category_id`)
WHERE c.`published` = 1
ORDER BY ca.`name`
my logic is obviously wrong because this query will return this:
category_id | category_name | alias | media_id | url | file_name | count
1 | One | one | 1 | /images/ | file1.jpg | 3
How can I achieve the desired result?
I think the most painless (and sane) way to approach this would be to just aggregate the product counts for each category from the product_category table in a separate subquery, and then just join this to what you already have:
SELECT
c.category_id,
ca.name,
ca.alias,
m.media_id,
ma.url,
ma.file_name,
COALESCE(t.cat_count, 0) AS cat_count
FROM category c
LEFT JOIN category_lang ca
ON c.category_id = ca.category_id
LEFT JOIN media m
ON c.category_id = m.category_id
LEFT JOIN media_lang ma
ON m.media_id = ma.media_id
LEFT JOIN
(
SELECT category_id, COUNT(*) AS cat_count
FROM product_category
GROUP BY category_id
) t
ON c.category_id = t.category_id
WHERE c.published = 1
ORDER BY ca.name
Note here that your product_category table does not have any category entries with no products. This is not a problem, because in the LEFT JOIN we can simply treat a NULL count as being zero. A NULL value would occur if a given category did not match to anything in the subquery.
Seems to me that it is where you are doing the count and that you doing have the criteria for doing the count. Adding an extra condition where product category id equals category id in your category table should fix the incorrect count.
SELECT
c.`category_id`,
ca.`name`,
ca.`alias`,
m.`media_id`,
ma.`url`,
ma.`file_name`,
COUNT(p.`product_id`) AS `count`
FROM `category` c
LEFT JOIN `category_lang` ca ON (c.`category_id` = ca.`category_id`)
LEFT JOIN `media` m ON (c.`category_id` = m.`category_id`)
LEFT JOIN `media_lang` ma ON (m.`media_id` = ma.`media_id`)
LEFT JOIN `product_category` p ON (c.`category_id` = p.`category_id`)
WHERE (c.`published` = 1) and (c.`category_id` = p.`category_id`)
ORDER BY ca.`name`
I'm a little bit confused about which join to apply and where.
I have a mysql database that does betting in an IRC client.
It stores usernames , their guess and the eventual outcome of the game they bet on
the outcomes_table is like this
+--------------+
| id outcome |
+--------------+
| 1 win |
| 2 lose |
+--------------+
the user_table is like this
+----+----------+----------+-------------------+
| id | username | guess_id | bettingsession_id |
+----+----------+----------+-------------------+
| 1 | name1 | 1 | 1 |
| 2 | name2 | 1 | 2 |
| 3 | name3 | 2 | 2 |
4 name1 1 2
+----+----------+----------+-------------------+
the betting_session_table is like this:
+----+---------+
| id | result |
+----+---------+
| 1 | 1 |
| 2 | 2 |
+----+---------+
I want to get a list of the bets of a user with their guess joined to the outcome and the result joined to the
eg:
select each row a different bet username, guess (win/lose), result (win/lose)
Something like:
SELECT *
FROM user_table
INNER JOIN betting_session_table ON bettingsession_id = betting_session_table.id
INNER JOIN outcomes_table ON guess_id = outcomes_table.id
INNER JOIN outcomes_table ON result = outcomes_table.id
WHERE username = 'name1'
However this doesn't work, not sure but I don't think it lets me join the outcomes_table.id twice to two different columns but I want to this because the user may bet 'win' but result 'lose' etc.
EG: I want to return
+----+----------+----------+----+---------+--------------------+----+--------+----+---------+
| id | username | guess_id | id | outcome | betting_session_id | id | result | id | outcome |
+----+----------+----------+----+---------+--------------------+----+--------+----+---------+
| 1 | name1 | 1 | 1 | win | 1 | 1 | 1 | 1 | win |
| 4 | name1 | 1 | 1 | win | 2 | 2 | 2 | 2 | lose |
+----+----------+----------+----+---------+--------------------+----+--------+----+---------+
EDIT:
In the end I used two separate alias for each join which seems to work; here is the code from the actual table that works rather than the cut down example above.
SELECT *
FROM `xcoins_betting_log` A
LEFT JOIN `xcoins_betting_session` B ON A.betting_session_id = B.id
LEFT JOIN `xcoins_common_tables`.`xcoins_betting_outcomes` C ON A.guess_id = C.id
LEFT JOIN `xcoins_common_tables`.`xcoins_betting_outcomes` D ON B.outcome_id = D.id
WHERE `user_id` =9
I'm not sure if this is what you want, but I hope so.
SELECT
usr.*,
res.outcome,
IF(res.id = usr.guess_id, 'User win', 'User lose') AS result
FROM user_table AS usr
INNER JOIN betting_session_table AS bet ON
bet.id = usr.bettingsession_id
INNER JOIN outcomes_table AS res ON
res.id = bet.result
WHERE usr.username = 'name1'
Choose correct join
The most common joins is LEFT and INNER. Lets say the users have placed their bets, but the football game (or whatever) isn't completed yet, then you won't have the row in the outcomes_table right? The game isn't finished so the results will come later.
If you use INNER JOIN, the row in the outcomes_table won't match for unfinished games --- INNER JOIN requires matches.
If you want to see the bets also before the game has started, you can use LEFT JOIN. LEFT JOIN won't remove rows that hasn't got any outcome, the users will still be listed --- LEFT JOIN doesn't care.
INNER JOIN: Game must have result
LEFT JOIN: Game might have result
I have two tables, one with tags and another one with the actually selected tags of an article (table relation). I want to add the condition to select the amount of the actually selected tags of the current article. example selecting article 1:
--------------------------
| id | name | selected |
--------------------------
| 0 | this | 1 |
| 1 | is | 0 |
| 2 | sparta | 1 |
--------------------------
Ive got to this far:
SELECT t.*, count(t.id) as `selected`
FROM tag t LEFT JOIN relation r ON t.id = r.tid
GROUP BY t.id
table tag:
---------------
| id | name |
---------------
| 0 | this |
| 1 | is |
| 2 | sparta |
---------------
table relation:
-------------
| tid | aid |
-------------
| 0 | 2 |
| 0 | 1 |
| 2 | 1 |
-------------
EDIT : The first query returns the selected tags for an article and that's not the desired behaviour
Is that the query you're looking for?
SELECT A.id
, COUNT(T.id) AS [nb selected tags]
FROM article A
LEFT OUTER JOIN relation R ON R.aid = A.id
LEFT OUTER JOIN tag T ON T.id = R.tid
AND T.selected = 1
GROUP BY A.id
Hope this will help you.
PS: If you just want the result of a specific Article, you juste have to add a WHERE clause before the GROUP BY.
Query returning the desired data:
SELECT T.id
,T.name
, CASE
WHEN R.tid IS NOT NULL THEN 1
ELSE 0
END AS [selected]
FROM tag T
LEFT OUTER JOIN relation R ON R.tid = T.id
AND R.selected = 1
AND R.aid = ...
I assume that the table relationhas a unicity on tid and aid.
Let me know if the query returns the expected result.
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)
I have 2 tables in my database: item and category.
Items can be active, or inactive, and have a categoryID that relates to the id of a record in the category table.
i want to perform a query to show all the categories, with the total cost of active items for the category
So my goal is to return something looking like this:
+--------+------------+---------------+
| id | cat_name | total_cost |
+--------+------------+---------------+
| 1 | cat 1 | 12 |
| 2 | cat 2 | 0 |
| 3 | cat 3 | 45 |
+--------+------------+---------------+
My first query:
SELECT a.*,
SUM(b.cost) AS total_cost
FROM categories a LEFT JOIN items b
ON(a.id = b.category_id)
GROUP BY a.category_name
works ok, but it returns NULL items instead of 0, and uses all items regardless of active/inactive:
+--------+------------+---------------+
| id | cat_name | total_cost |
+--------+------------+---------------+
| 1 | cat 1 | 44 |
| 2 | cat 2 | NULL |
| 3 | cat 3 | 87 |
+--------+------------+---------------+
my second query adresses the NULL values:
SELECT a.*,
SUM(IF(b.cost IS NULL, 0, b.cost)) AS total_cost
FROM categories a LEFT JOIN items b
ON(a.id = b.category_id)
GROUP BY a.category_name
and turns out like so:
+--------+------------+---------------+
| id | cat_name | total_cost |
+--------+------------+---------------+
| 1 | cat 1 | 44 |
| 2 | cat 2 | NULL |
| 3 | cat 3 | 87 |
+--------+------------+---------------+
So in my tiny useless brain i try the following query, adding a WHERE clause on table b where active has to = 1 (true)
SELECT a.*,
SUM(IF(b.cost IS NULL, 0, b.cost)) AS total_cost
FROM categories a LEFT JOIN items b
ON(a.id = b.category_id)
WHERE b.active = 1
GROUP BY a.category_name
and i get the following:
+--------+------------+---------------+
| id | cat_name | total_cost |
+--------+------------+---------------+
| 1 | cat 1 | 12 |
| 3 | cat 3 | 45 |
+--------+------------+---------------+
so as you can se, i would like to return the entire range of categories, even when the right table returns no matching results... Any takes for a million imaginary cool points?
Use:
SELECT c.id,
c.cat_name,
COALESCE(SUM(i.cost), 0) AS total_cost
FROM CATEGORIES c
LEFT JOIN ITEMS i ON i.category_id = c.category_id
AND i.active = 1
GROUP BY c.id, c.cat_name
Try this:
SELECT a.*,
SUM(Case B.Active When 1 Then b.cost else 0 End) AS total_cost
FROM categories a
LEFT JOIN items b
ON b.category_id = a.id
GROUP BY a.category_name
or this:
SELECT a.*, SUM(b.cost) AS total_cost
FROM categories a
LEFT JOIN items b
ON b.category_id = a.id
And B.Active = 1
GROUP BY a.category_name