Mysql REGEXP 2 words -- Same as AND LIKE - mysql

I have
SELECT p.*, gc.*, g.*, m.*, b.*
FROM (products AS p)
LEFT JOIN merchants AS m
ON m.merchantId = p.merchantId
LEFT JOIN brands AS b
ON b.brand = p.brand
LEFT JOIN groups_content gc
ON p.brand = gc.brandsSelect
LEFT JOIN groups g
ON g.group_id = gc.group_id
WHERE `p`.`percentOff` > gc.percent_off
AND `g`.`group_active` = 1
AND `p`.`price_sale` BETWEEN gc.price_min
AND gc.price_max
AND `gc`.`group_content_id` = '180'
AND `p`.`keyword` LIKE '%women%' AND `p`.`keyword` LIKE '%jacket%'
AND `p`.`status` = 1
ORDER BY p.price_sale ASC
this return me 158 results
Also I have
SELECT p.*, gc.*, g.*, m.*, b.*
FROM (products AS p)
LEFT JOIN merchants AS m
ON m.merchantId = p.merchantId
LEFT JOIN brands AS b
ON b.brand = p.brand
LEFT JOIN groups_content gc
ON p.brand = gc.brandsSelect
LEFT JOIN groups g
ON g.group_id = gc.group_id
WHERE `p`.`percentOff` > gc.percent_off
AND `g`.`group_active` = 1
AND `p`.`price_sale` BETWEEN gc.price_min
AND gc.price_max
AND `gc`.`group_content_id` = '180'
AND `p`.`keyword` REGEXP 'women.+jacket'
AND `p`.`status` = 1
ORDER BY p.price_sale ASC
This should return me same result like previous. But returns me just 17 results. What I am doing wrong?
----------
AFTER MANY SEARCHES here it is the right REGEXP
AND p.`keyword` REGEXP '^(.+jacket.+women.+)|^(.+women.+jacket.+).*$'

Your regex and like are not equivalent statements:
CREATE TABLE tab(keyword VARCHAR(100));
INSERT INTO tab(keyword)
VALUES ('jacket for women');
SELECT *
FROM tab p
WHERE `p`.`keyword` LIKE '%women%' AND `p`.`keyword` LIKE '%jacket%';
-- jacket for women
SELECT *
FROM tab p
WHERE p.`keyword` REGEXP 'women.+jacket';
-- (empty)
SqlFiddleDemo
"woman" anywhere in string and "jacket" anywhere in string
vs.
"woman" at the beginning, then any characters then "jacket" at the end
EDIT:
But How do I get -- "woman" anywhere in string and "jacket" anywhere
in string --- using regular expression?
One way is to use:
SELECT *
FROM tab
WHERE keyword REGEXP '.*women.*'
AND keyword REGEXP '.*jacket.*';
I am sure there exists one regex for this case.

Related

Get a specific combination from product with given attributes in Prestashop

I building a custom SQL query for my module to retrieve all combinations of a product with id_product and multiple attributes ids, but currently, I only managed to select it with one attribute and no more, I'm really missing something but didn't find it yet.
To get in context here's my query to find all combinations (color & size) of a product and its result:
SELECT
p.id_product,
pq.quantity,
pa.price AS price_diff,
p.price,
pai.id_image,
pl.name,
GROUP_CONCAT(agl.id_attribute_group, ':', pal.id_attribute ORDER BY agl.id_attribute_group SEPARATOR ", ") as combination_ids,
GROUP_CONCAT(pal.name ORDER BY agl.id_attribute_group SEPARATOR ", ") as combination
FROM ps_product p
LEFT JOIN ps_product_attribute pa ON (p.id_product = pa.id_product)
LEFT JOIN ps_stock_available pq ON (p.id_product = pq.id_product AND pa.id_product_attribute = pq.id_product_attribute)
LEFT JOIN ps_product_lang pl ON (p.id_product = pl.id_product)
LEFT JOIN ps_product_attribute_combination pac ON (pa.id_product_attribute = pac.id_product_attribute)
LEFT JOIN ps_attribute_lang pal ON (pac.id_attribute = pal.id_attribute)
LEFT JOIN ps_attribute a ON (pal.id_attribute = a.id_attribute)
LEFT JOIN ps_attribute_group_lang agl ON (a.id_attribute_group = agl.id_attribute_group)
LEFT JOIN ps_product_attribute_image pai on(pa.id_product_attribute = pai.id_product_attribute)
WHERE pl.id_lang = 1
AND pal.id_lang = 1
AND agl.id_lang = 1
AND p.id_product = 3196 -- My product
GROUP BY pac.id_product_attribute
The result
Query with a single attribute (size S for this example):
......................
......................
AND p.id_product = 3196 -- My product
AND agl.id_attribute_group = 9 -- size
AND pal.id_attribute = 761 -- 'S' size for my case
GROUP BY pac.id_product_attribute
But no success with specifying both size AND color, any idea?
I think you want a HAVING clause. To filter on two attributes, the logic would be:
SELECT ...
FROM ...
WHERE ...
GROUP BY pac.id_product_attribute
HAVING
MAX(agl.id_attribute_group = 9 AND pal.id_attribute = 761) = 1
AND MAX(agl.id_attribute_group = 2 AND pal.id_attribute = 727) = 1
I should warn that your code is not a valid aggregation query. You need more column in the GROUP BY clause to fix that flaw. It is hard to tell for sure without seeing your data, but, with a few assumptions on the primary key of each table:
GROUP BY
p.id_product,
pa.id_product_attribute,
pac.id_product_attribute,
pai.id_image,
pq.id -- if that exists?

Extracting latest version of articles from the results of a previous query

I have the following query:
SELECT e_c.*, c.name, j.status, j.version, j.articleId, j.title FROM assetcategory AS c
INNER JOIN assetentries_assetcategories AS e_c
ON c.categoryId = e_c.categoryId AND c.name = 'news'
INNER JOIN assetentry AS e
ON e.entryId = e_c.entryId
INNER JOIN journalarticle AS j
ON j.resourcePrimKey = e.classPK
AND e.classNameId = (SELECT classNameId FROM classname_ WHERE value = 'com.liferay.portlet.journal.model.JournalArticle')
AND j.companyId= e.companyId
WHERE j.status = 0
which returns all the category news in the journalarticles. From the results I need to select the most recent versions for each articleId. For example suppose there is an article with 4 versions, even with different title, it is the same article because it will have the same articleId. So therefore for each unique articleId I need the latest version. How can I do that?
Add a join to a subquery which finds the most recent version for each article:
SELECT e_c.*, c.name, j1.status, j1.version, j1.articleId, j1.title
FROM assetcategory AS c
INNER JOIN assetentries_assetcategories AS e_c
ON c.categoryId = e_c.categoryId AND c.name = 'news'
INNER JOIN assetentry AS e
ON e.entryId = e_c.entryId
INNER JOIN journalarticle AS j1
ON j1.resourcePrimKey = e.classPK AND
e.classNameId = (SELECT classNameId FROM classname_
WHERE value = 'com.liferay.portlet.journal.model.JournalArticle') AND
j.companyId = e.companyId
INNER JOIN
(
SELECT articleId, MAX(version) AS max_version
FROM journalarticle
WHERE status = 0
GROUP BY articleId
) j2
ON j1.articleId = j2.articleId AND j1.version = j2.max_version;
The basic idea behind the join to the subquery aliased as j2 above is that it restricts the result set to only the most recent version of each article. We don't necessarily have to change the rest of the query.

Or operator returns more rows then expected mysql

I have a table with some items that are related to car models. Those items could have several categories. I have to select all of them that are related to cars and in all given categories categories for each item stored in sc_products table.
Here is my query:
SELECT
t15_catalogue_line.
T15_GROUP,
sc_products.product_code,
sc_products.unic, sc_products.name_ru, UPPER(TRIM(sc_products.brief_description_ru)) AS brief_description_ru, sc_products.suupplier, price.Price, sc_group_discounts.`action`, sc_group_discounts.procent, sc_products.productID, price.in_stock, price.supplier, t10_item.T10_ITEM, t10_item.unic, t10_item.T10_DESC, t10_item.T10_IMG, t10_item.T10_ITEM_GROUP, t10_item.T10_FIELD1, t10_item.T10_FIELD2, t10_item.T10_FIELD3, t10_item.T10_FIELD4, t10_item.T10_FIELD5, t10_item.T10_FIELD6, t10_item.T10_FIELD7, t10_item.T10_FIELD8, t10_item.T10_FIELD9, t10_item.T10_FIELD10, t10_item.T10_FIELD11, t14_item_fields.T14_ITEM_GROUP, t14_item_fields.T14_FIELD, t14_item_fields.T14_NAME, t14_item_fields.T14_UNIT, t14_item_fields.T14_SEARCH
FROM
sc_products
LEFT OUTER JOIN t15_catalogue_line ON (sc_products.unic = t15_catalogue_line.unic)
LEFT OUTER JOIN price ON (sc_products.unic = price.unic) AND (sc_products.suupplier = price.postavchik)
LEFT OUTER JOIN sc_group_discounts ON (sc_products.item_group = sc_group_discounts.item_group)
LEFT OUTER JOIN t10_item ON (sc_products.unic = t10_item.unic and sc_products.CatText=t10_item.CatText and sc_products.brief_description_ru=t10_item.brand)
LEFT OUTER JOIN t14_item_fields ON (t10_item.T10_ITEM_GROUP = t14_item_fields.T14_ITEM_GROUP)
WHERE
sc_products.CatText = 'bracke mechanism' or
sc_products.CatText='bracke montage ' or
sc_products.CatText='hydraulic repair ' AND
t15_catalogue_line.T15_CARTYPE = '30442' AND
t15_catalogue_line.T15_GROUP = '666' and
sc_products.unic is not null and
sc_products. unic!=''
GROUP BY sc_products.product_code,sc_products.brief_description_ru, sc_products.suupplier
ORDER BY ISNULL( price.price),price.price ASC
Query returns all items in those caegories and they aren't related to auto so table t15_catalog line doesn't take part in query what could be the solution
I highly suspect that the missing parentheses () around the multiple AND is your problem.
Query returns all items in those caegories and they aren't related to auto so table t15_catalog line doesn't take part in query what could be the solution
Your grouping logic now does exactly what you describe, below is what i think you meant to do:
WHERE (sc_products.CatText = 'bracke mechanism' or sc_products.CatText='bracke montage ' or sc_products.CatText='hydraulic repair ') AND t15_catalogue_line.T15_CARTYPE = '30442' AND t15_catalogue_line.T15_GROUP = '666' and sc_products.unic is not null and sc_products. unic!='' GROUP BY sc_products.product_code,sc_products.brief_description_ru, sc_products.suupplier ORDER BY ISNULL( price.price),price.price ASC
This logic:
a OR b OR c AND d is equivalent to a OR b OR (c AND d) and to not (a OR b OR c) AND d which seems to be what you want.
You need to add parentheses around the OR block like this:
WHERE
(
sc_products.CatText = 'bracke mechanism'
OR sc_products.CatText = 'bracke montage '
OR sc_products.CatText = 'hydraulic repair '
)
AND t15_catalogue_line.T15_CARTYPE = '30442'
AND t15_catalogue_line.T15_GROUP = '666'
AND sc_products.unic is not null
AND sc_products. unic!=''
You probably want to use paranthesis in your query for the desired result. How about
SELECT
t15_catalogue_line.
T15_GROUP,
sc_products.product_code,
sc_products.unic, sc_products.name_ru,
UPPER(TRIM (sc_products.brief_description_ru)) AS brief_description_ru,
sc_products.suupplier, price.Price, sc_group_discounts.`action`,
sc_group_discounts.procent, sc_products.productID,
price.in_stock, price.supplier, t10_item.T10_ITEM, t10_item.unic,
t10_item.T10_DESC, t10_item.T10_IMG, t10_item.T10_ITEM_GROUP,
t10_item.T10_FIELD1, t10_item.T10_FIELD2, t10_item.T10_FIELD3,
t10_item.T10_FIELD4, t10_item.T10_FIELD5, t10_item.T10_FIELD6,
t10_item.T10_FIELD7, t10_item.T10_FIELD8, t10_item.T10_FIELD9,
t10_item.T10_FIELD10, t10_item.T10_FIELD11,
t14_item_fields.T14_ITEM_GROUP, t14_item_fields.T14_FIELD,
t14_item_fields.T14_NAME, t14_item_fields.T14_UNIT,
t14_item_fields.T14_SEARCH
FROM
sc_products
LEFT OUTER JOIN t15_catalogue_line ON
(sc_products.unic = t15_catalogue_line.unic)
LEFT OUTER JOIN price ON
(sc_products.unic = price.unic)
AND (sc_products.suupplier = price.postavchik)
LEFT OUTER JOIN sc_group_discounts ON
(sc_products.item_group = sc_group_discounts.item_group)
LEFT OUTER JOIN t10_item ON
(sc_products.unic = t10_item.unic and
sc_products.CatText=t10_item.CatText and
sc_products.brief_description_ru=t10_item.brand)
LEFT OUTER JOIN t14_item_fields ON
(t10_item.T10_ITEM_GROUP = t14_item_fields.T14_ITEM_GROUP)
WHERE
(
sc_products.CatText = 'bracke mechanism'
or
sc_products.CatText='bracke montage '
or
sc_products.CatText='hydraulic repair '
)
AND t15_catalogue_line.T15_CARTYPE = '30442'
AND t15_catalogue_line.T15_GROUP = '666'
and sc_products.unic is not null
and sc_products. unic!=''
GROUP BY
sc_products.product_code,sc_products.brief_description_ru,
sc_products.suupplier ORDER BY ISNULL( price.price),
price.price ASC

How to remove duplicates from a SQL query based on a single column

There's a query I need to modify. What the query currently does is return search results (ads) based on Ad Title and Ad Description . If any of the search words are either found in ad title or ad description, it returns those results
I want to modify the query so that each ad appears in search results only once for a given ad title... So if there were 5 ads found with the same ad title for the given words in the search , it should return only 1 ad for that ad title...
$sql = "SELECT a.*, UNIX_TIMESTAMP(a.createdon) AS timestamp, ct.cityname,
COUNT(*) AS piccount, p.picfile,
scat.subcatname, cat.catid, cat.catname $xfieldsql
FROM t_ads a
INNER JOIN t_cities ct ON a.cityid = ct.cityid
INNER JOIN t_subcats scat ON a.subcatid = scat.subcatid
INNER JOIN t_cats cat ON scat.catid = cat.catid
LEFT OUTER JOIN t_adxfields axf ON a.adid = axf.adid
LEFT OUTER JOIN t_adpics p ON a.adid = p.adid AND p.isevent = '0'
LEFT OUTER JOIN t_featured feat ON a.adid = feat.adid AND feat.adtype = 'A'
WHERE $where
AND $visibility_condn
AND (feat.adid IS NULL OR feat.featuredtill < NOW())
$loc_condn
GROUP BY a.adid
ORDER BY a.createdon DESC
LIMIT $offset, $ads_per_page";
Edit: $where contains the search expression... if regular expression search is turned on it uses regex otherwise not... $sqlsearch contains the search words that were input by the user...
if ($regex_search) {
$where = "(a.adtitle RLIKE '[[:<:]]{$searchsql}[[:>:]]' OR a.addesc RLIKE '[[:<:]]{$searchsql}[[:>:]]')";
} else {
$where = "(a.adtitle LIKE '$searchsql' OR a.addesc LIKE '$searchsql')";
The "proper" way to do this would be tackle the route cause by working out why the duplicates are appearing in the first place. It will be something to do with the JOINs but without looking at the data I'm unable to answer that. If, however you'd like a quick(ish) and dirty way to remove duplicates, could try something like below.
Disclaimer: This is completely untested so there's more likely to be a mistake or two in here - but hopefully no dealbreaker.
SELECT a2.*, UNIX_TIMESTAMP(a.createdon) AS timestamp, ct2.cityname,
COUNT(*) AS piccount, p2.picfile,
scat2.subcatname, cat2.catid, cat2.catname $xfieldsql
FROM
(SELECT subq1.title, MIN(subq1.adid) AS adid
FROM
(SELECT a.*, UNIX_TIMESTAMP(a.createdon) AS timestamp, ct.cityname,
COUNT(*) AS piccount, p.picfile,
scat.subcatname, cat.catid, cat.catname
FROM t_ads a
INNER JOIN t_cities ct ON a.cityid = ct.cityid
INNER JOIN t_subcats scat ON a.subcatid = scat.subcatid
INNER JOIN t_cats cat ON scat.catid = cat.catid
LEFT OUTER JOIN t_adxfields axf ON a.adid = axf.adid
LEFT OUTER JOIN t_adpics p ON a.adid = p.adid AND p.isevent = '0'
LEFT OUTER JOIN t_featured feat ON a.adid = feat.adid AND feat.adtype = 'A'
WHERE $where
AND $visibility_condn
AND (feat.adid IS NULL OR feat.featuredtill < NOW())
$loc_condn
GROUP BY a.adid) subq1
GROUP BY subq.title) subq2
INNER JOIN t_ads a2 ON a2.adid = subq2.adid
INNER JOIN t_cities ct2 ON a2.cityid = ct2.cityid
INNER JOIN t_subcats scat2 ON a2.subcatid = scat2.subcatid
INNER JOIN t_cats cat2 ON scat2.catid = cat2.catid
LEFT OUTER JOIN t_adxfields axf2 ON a2.adid = axf2.adid
LEFT OUTER JOIN t_adpics p2 ON a2.adid = p2.adid AND p2.isevent = '0'
LEFT OUTER JOIN t_featured feat2 ON a2.adid = feat2.adid AND feat2.adtype = 'A'
ORDER BY a2.createdon DESC
LIMIT $offset, $ads_per_page
This could be massively simplified and tidied up e.g. by removing some of the stuff from the subquery but am just giving the general idea to (hopefully) get you up and running...
Explanation
subq2 simply groups by title and picks out an adid from each group (chose to use MIN here but could have used MAX instead).
subq1 is the original query but with ordering and limits removed since these are applied by the outer query.
The outer query joins back on the de-duped IDs and joins back to the ads and other tables (giving them different aliases) in order to select the fields from your original query.

How to write a select statement inside another select in SQL

Can anyone tell me what is wrong with this query?
it gives a syntax error near the 2nd select
SELECT b.mc_boxes_idmc_boxes,
t.idtitles,
t.title,
t.languages_idlanguages,
MAX(h.idtitle_history),
MAX(h.edition)
(SELECT h.preview, h.file WHERE h.idtitle_history = MAX(h.idtitle_history))
FROM mc_boxes_has_titles b
LEFT JOIN titles t ON b.titles_idtitles = t.idtitles
LEFT JOIN title_history h ON h.titles_idtitles = t.idtitles
WHERE b.mc_boxes_idmc_boxes = 12
AND h.edition IS NOT NULL
GROUP BY b.mc_boxes_idmc_boxes, idtitles
ORDER BY b.sortorder;
looks like you are missing a comma after MAX(h.edition)
SELECT b.mc_boxes_idmc_boxes,
t.idtitles,
t.title,
t.languages_idlanguages,
MAX(h.idtitle_history),
MAX(h.edition),
(SELECT h.preview, h.file WHERE h.idtitle_history = MAX(h.idtitle_history))
FROM mc_boxes_has_titles b
LEFT JOIN titles t ON b.titles_idtitles = t.idtitles
LEFT JOIN title_history h ON h.titles_idtitles = t.idtitles
WHERE b.mc_boxes_idmc_boxes = 12
AND h.edition IS NOT NULL
GROUP BY b.mc_boxes_idmc_boxes, idtitles
ORDER BY b.sortorder;
besides the comma, you are selecting two fields in your subquery
SELECT b.mc_boxes_idmc_boxes,
t.idtitles,
t.title,
t.languages_idlanguages,
MAX(h.idtitle_history),
MAX(h.edition),
(SELECT preview FROM title_history WHERE idtitle_history = MAX(h.idtitle_history)),
(SELECT [file] FROM title_history WHERE idtitle_history = MAX(h.idtitle_history))
FROM mc_boxes_has_titles b
LEFT JOIN titles t ON b.titles_idtitles = t.idtitles
LEFT JOIN title_history h ON h.titles_idtitles = t.idtitles
WHERE b.mc_boxes_idmc_boxes = 12
AND h.edition IS NOT NULL
GROUP BY b.mc_boxes_idmc_boxes, idtitles
ORDER BY b.sortorder;
Adding to bluefeet's answer, you may want to check for reserved words. "File" for example is a reserved word in sql server.
Alias it as a virtual table. Change something like
SELECT b.mc_boxes_idmc_boxes,
t.idtitles,
t.title,
t.languages_idlanguages,
MAX(h.idtitle_history),
MAX(h.edition)
(SELECT h.preview, h.file WHERE h.idtitle_history = MAX(h.idtitle_history))
FROM mc_boxes_has_titles b
LEFT JOIN titles t ON b.titles_idtitles = t.idtitles
LEFT JOIN title_history h ON h.titles_idtitles = t.idtitles
WHERE b.mc_boxes_idmc_boxes = 12
AND h.edition IS NOT NULL
GROUP BY b.mc_boxes_idmc_boxes, idtitles
ORDER BY b.sortorder) as virtual_column_alias;
Check this out. Hope this will work.