MySQL Join two tables with condition - mysql

Based on these two tables:
products
| ID | Active | Name | No
--------------------------------------------------
| 1 | 1 | Shirt | 100
| 2 | 0 | Pullover | 200
variants
| MasterID | Active | Name | No
--------------------------------------------------
| 1 | 1 | Red | 101
| 1 | 0 | Yellow | 102
I want to get every product which is active and also their active variants in one sql.
Relation between those tables MasterID -> ID
Needed result:
ID (master) | Name | No
--------------------------------------------------
1 | Shirt | 100
1 | Red | 101
I tried it with using union, but then I am not able to get the belonging MasterIDs.

It looks like you just need a simple join:
select *
from products
left join variants
on products.ID = variants.MasterID
where products.Active = 1
and variants.Active = 1
Update after requirements were made clearer:
select ID, Name, No, 'products' as RowType
from products
where Active = 1
union
select variants.MasterID as ID, variants.Name, variants.No, 'variants' as RowType
from products
join variants
on products.ID = variants.MasterID
where products.Active = 1
and variants.Active = 1
order by ID, RowType, No
I've assumed you want the results ordered by ID, with products followed by variants. The No column may order it this way implicitly (it's impossible to know without real data), in which case the RowType column can be removed. The order by clause might need to be altered to match your specific RDBMS.

This should gives you the expected result:
select * from products left join variants on products.id = variants.masterId
where products.active=1 and variants.active=1
If not please add the expected result to your question.

Related

How to exclude rows when a certain condition is met in MySQL

I have two datatables. One table contains information of participants (Participants). And a table which contains all registrations for the event (Registrars).
+----+-------+
| id | name |
+----+-------+
| 1 | Peter |
+----+-------+
| 2 | John |
+----+-------+
+-----------+----------+
| person_id | event_id |
+-----------+----------+
| 1 | 1 |
+-----------+----------+
| 2 | 2 |
+-----------+----------+
| 2 | 1 |
+-----------+----------+
I run a MySQL query to get the information of registrars of an event. The query looks like this:
SELECT name
FROM Participants
INNER JOIN Registrars
ON Participants.id=Registrars.person_id
WHERE
event_id = 1
Now I want to build a query where if the participant also registered for event 2 it will not be returned in the result of the query the reg. How can I achieve this in one query?
You can group by participant and use conditional aggregation in the HAVING clause to set the conditions:
SELECT p.id, p.name
FROM Participants p INNER JOIN Registrars r
ON p.id = r.person_id
WHERE r.event_id IN (1, 2)
GROUP BY p.id, p.name
HAVING MAX(r.event_id = 1) = 1
AND MAX(r.event_id = 2) = 0
You can extend the code by adding more conditions in a similar way.
SELECT name
FROM Participants as p
INNER JOIN Registrars as reg
ON P.id=Reg.person_id
WHERE
reg.event_id = 1
and not exists (select 1 from Registrars as strr where strr.event_id=2 and p.id=strr.person_id )

Joining tables with different value prefix

I'm trying to join two tables but the problem is that in the second table the value that is the same as in table one has a prefix to it(this tables are generated after opencart instalation - demo data):
Table 1: category
-----------------------------
| category_id | category_name |
|-----------------------------|
| 1 | Components |
| 2 | Laptops |
Table 2: seo_url
------------------------------------------
| seo_url_id | query | keyword |
|------------------------------------------|
| 35 | category_id=1 | components |
| 78 | category_id=2 | laptops |
So the id of a category is in column category_id in Table 1 and it is a number but in Table 2 it is in column query and it has a prefix of category_id= and then the id x(in case of category laptops x being 2).
Can somebody please help me understand how i could join this tables in this situation?
So far i was trying to add category_id= + like this:
SELECT a.id, a.category_name, b.query
FROM category AS a
INNER JOIN seo_url AS b
ON a.category_id = 'category_id=' + b.query
P.S I tried ON 'category_id=' + a.category_id
P.S.S There are also product_id so i don't know if i could use LIKE but i was thinking about it, searched for it and couldn't find a way to make it work.
Thank you! D:
In MySQL, use the function CONCAT(...) that can append strings and numbers, and it is compatible with different versions of that database.
Your fixed query would be:
SELECT a.id, a.category_name, b.query
FROM category AS a
INNER JOIN seo_url AS b
ON CONCAT('category_id=', a.category_id) = b.query;
Your table seo_url already has the 'category_id=' in the values of the field query, so you don't need to append it.
Additionally, I'd recommend you to name the table aliases with more representative names, instead of using a and b.
Hope this helps you to solve your problem!
Use nested REVERSE functions with + 0 to autocast "parse" out the integer.
Query
SELECT
REVERSE(REVERSE('category_id=2') + 0)
UNION ALL
SELECT
REVERSE(REVERSE('category_id=21') + 0)
Result
| REVERSE(REVERSE('category_id=2') + 0) |
|---------------------------------------|
| 2 |
| 21 |
see demo http://sqlfiddle.com/#!9/340e01/530
Use it in your query.
Query
SELECT a.category_id, a.category_name, b.query
FROM category AS a
INNER JOIN seo_url AS b
ON a.category_id = REVERSE(REVERSE(b.query) + 0)
Result
| category_id | category_name | query |
|-------------|---------------|---------------|
| 1 | Components | category_id=1 |
| 2 | Laptops | category_id=2 |
see demo http://sqlfiddle.com/#!9/ef5781/1

Modify MySQL Query or Run in PHP

I'm trying to run a query to find which inventory I should promote and which campaign I should run so I can move that inventory.
I have three tables:
campaigns lists different campaigns that I can run, each campaign has a unique id. Some campaigns promote only one item and some promote multiple items.
inventory has all the items I have in stock and the quantity of those items.
campaign_to_inventory matches the unique campaign id to the inventory item.
campaigns:
name | id
-------------|---
blue-widgets | 1
gluten-free | 2
gadget | 3
inventory:
item | qty
-------|----
thing1 | 0
thing2 | 325
thing3 | 452
thing5 | 123
thing7 | 5
campaign_to_inventory:
id | item
---|-------
1 | thing1
1 | thing2
1 | thing5
2 | thing1
2 | thing3
3 | thing7
I'd like to run a query to find all the campaigns I could run where I have the needed inventory in stock. I'm currently running this query:
SELECT * FROM `campaigns` LEFT JOIN `campaign_to_inventory` ON `campaigns`.`id` = `campaign_to_inventory`.`id` LEFT JOIN `inventory` ON `campaign_to_inventory`.`item` = `inventory`.`item`
Which returns:
name | id | item | qty
-------------|----|--------|----
blue-widgets | 1 | thing1 | 0
blue-widgets | 1 | thing2 | 325
blue-widgets | 1 | thing5 | 123
gluten-free | 2 | thing1 | 0
gluten-free | 2 | thing3 | 452
gadget | 3 | thing7 | 5
Should I use PHP to process this data to find only campaigns where all item quantities are greater than a minimum threshold, or is there a way to modify the query to limit the rows there? Is there a rule of thumb of when I can/should do it in one and not the other?
There's no need to process the data in PHP.
One way to do this would be to select the campaign_to_inventory.id column where the number of items is less than your threshold, like this:
SET #min_qty = 1;
SELECT `c_to_i`.`id` FROM `campaign_to_inventory` AS `c_to_i`
INNER JOIN `inventory` ON `inventory`.`item` = `c_to_i`.`item`
WHERE `inventory`.`qty` <= #min_qty;
... And then do a left outer join from campaign_to_inventory to that like this:
SET #min_qty = 1;
SELECT `id`, `name` FROM `campaigns`
LEFT JOIN (
/* Table of campaigns which contain items with not enough qty*/
SELECT `c_to_i`.`id` FROM `campaign_to_inventory` AS `c_to_i`
INNER JOIN `inventory` ON `inventory`.`item` = `c_to_i`.`item`
WHERE `inventory`.`qty` <= #min_qty
) AS `campaigns_with_not_enough_items`
ON `campaigns`.`id` = `campaigns_with_not_enough_items`.`id`
WHERE `campaigns_with_not_enough_items`.`id` is NULL;
The result should be a table of campaigns which have the needed inventory in stock.
As an aside, you should rename your campaign_to_inventory.id column to campaign since the name id implies that the column is the primary key for the table.

MySQL. Group matches in columns by a common id

I am codding a search page with multiple filters and I am wondering if this is the best approach to get the results.
Each result of the search has several attributes, here I am using two attributes to simplify the example.
The main 'items' table:
id_items
1
2
The 'languages' table:
id_languages | language_code
1 es
2 en
The 'attributes_one' table:
id_attributes_one
1
2
The 'attributes_one_translations' table:
id_attributes_one_translations | id_attributes_one | id_language_code | translation
1 | 1 | 1 | Oro
2 | 1 | 2 | Gold
3 | 2 | 1 | Plata
4 | 2 | 2 | Silver
The 'attributes_one_match' table:
id_attributes_one_match | id_attributes_one | id_items
1 | 1 | 1
2 | 2 | 1
3 | 1 | 2
The 'attributes_two' table:
id_attributes_two
1
The 'attributes_two_translations' table:
id_attributes_two_translations | id_attributes_two | id_language_code | translation
1 | 1 | 2 | 99% gold
The 'attributes_two_match' table:
id_attributes_two_match | id_attributes_two | id_items
1 | 1 | 1
The concept is one item can have 0 or more match of each attribute table, and that match can have 0 or more translations.
Here is the query I am using when the user selects the filters to get all the items that have the attribute_one 'Gold' or 'Silver' order by this attribute ascendant:
SELECT
i.id_items AS id,
GROUP_CONCAT(DISTINCT aot.translation ORDER BY aot.translation DESC SEPARATOR '!¡') AS attribute_one,
GROUP_CONCAT(DISTINCT att.translation ORDER BY att.translation DESC SEPARATOR '!¡') AS attribute_two
FROM
items i
LEFT JOIN
languages AS l ON l.language_code = 'en'
LEFT JOIN
attributes_one_match AS aom ON aom.id_items = i.id_items
LEFT JOIN
attributes_one_translations AS aot ON aot.id_attributes_one = aom.id_attributes_one
AND l.id_languages = aot.id_language_code
AND (MATCH (aot.translation) AGAINST ('"Gold"' IN BOOLEAN MODE)
OR MATCH (aot.translation) AGAINST ('"Silver"' IN BOOLEAN MODE))
LEFT JOIN
attributes_one AS ao ON ao.id_attributes_one = aom.id_attributes_one
LEFT JOIN
attributes_two_match AS atm ON atm.id_items = i.id_items
LEFT JOIN
attributes_two_translations AS att ON att.id_attributes_two = atm.id_attributes_two
AND l.id_languages = att.id_language_code
LEFT JOIN
attributes_two AS at ON at.id_attributes_two = atm.id_attributes_two
GROUP BY id
ORDER BY 2 ASC
The result I get is:
id | attribute_one | attribute_two
2 | Gold | null
1 | Silver!¡Gold | 99% gold
That result is what I was expecting. Now:
* The table items will have around 300k entries once the data base is filled.
* There are 28 attributes table to match with the item.
Each attribute table will have around 20k entries, and each translation table will have 2
times the entries of the table that represents.
* Each item will have from 0 to 20 match to each item table, so I think
I wont have problems using the function GROUP_CONCAT
I am concern about the performance because the search filter page I am doing updates itself by ajax each time the user change one of the filters (it updates the filters and the results). The max results per page will be 1000 items, I didn't put the LIMIT in the query of the example.
I am not an sql expert so I don't really know if what I am doing is the best approach. I would appreciate some feedback.
Thanks a lot!

MySQL query when grouping duplicate records

I am trying to write a query that will find all of our sku's that have option that are a mix of drop downs and swatches.
We have a table that stores this info but is set up a little weird. The column that are important to me are.
-------------------------------------------
sku | option_flag | swatch_flag | sequence
-------------------------------------------
There can be multiple duplicate records of sku because the other columns change depending of how the sku is set up.
My expected results would look like this.
-------------------------------------------
sku | option_flag | swatch_flag | sequence
-------------------------------------------
aa001 | YES | NO | 1
-------------------------------------------
aa001 | YES | YES | 2
-------------------------------------------
aa001 | YES | NO | 3
-------------------------------------------
So far I have this and it is grouping by the sku but it is still including no for option_flag. Maybe instead of grouping by sku, is there a way to ask to sort it by the sequence 1 2 3 - 1 2 3 etc.
SELECT a.*
FROM sku_parent_attributes AS a
INNER JOIN
(SELECT sku
FROM sku_parent_attributes
WHERE option_flag = 'yes'
GROUP BY sku
HAVING COUNT(sku) = 3) AS b
ON a.sku = b.sku;
try this:
SELECT a.*,b.sku
FROM sku_parent_attributes AS a
INNER JOIN
sku_parent_attributes as b
ON a.sku = b.sku
GROUP BY b.sku
HAVING COUNT(b.sku) = 3
the where condition inside the full query and not only inside of the subquery.