I have
first:
select url from urls where site = '$value' order by url
second:
select id, widget_name from widgets where display_urls LIKE 'urls.url' and is_father=1 order by id
Third:
select id, widget_name from widgets where display_urls RLIKE 'urls.url' and is_father=0 order by id
Fourth:
select id, widget_name, father_widget from widgets where father_widget = 'widgets.id' order by id
The fourth query must only take widgets.id from second query
the second and third queries take the value of urls.url from the first query
how to write all those in one mysql query using left join ?
Thanks in advance for your answers :)
SELECT w.id, w.widget_name, c.*
FROM urls
JOIN widgets w
ON w.display_urls LIKE urls.url
OR w.display_urls RLIKE urls.url
LEFT JOIN
widgets с
ON c.father_widget = w.id
AND w.display_urls LIKE urls.url
AND w.display_urls NOT RLIKE urls.url
WHERE site = $value
ORDER BY
url
This will select children only for those widgets that are LIKE but nor RLIKE the corresponding url.
Related
I have three tables that are structured as follows:
results:
id | url_id
foo
id | url_id | url
bar
id | url_id | url
I want to be able to select the URL in a single query knowing that it could be in either table.
My plan was to do a LEFT JOIN on both of the tables and then return something like IFNULL(foo.url, bar.url) as url.
It is important that the result is called url.
However, I cannot use this same url column name in my WHERE clause because of the ambiguity. Here is my query and the error message I get:
SELECT r.id,
COALESCE(foo.url, bar.url) as url
FROM results r
LEFT JOIN foo USING (url_id)
LEFT JOIN bar USING (url_id)
WHERE url IS NOT NULL;
Column 'url' in where clause is ambiguous
It would be fairly trivial to select the column under a different name and rename it in my code, but it would be nicer to do it in SQL.
Here is an SQL Fiddle: http://sqlfiddle.com/#!9/32abc6/4
Cheers
You cannot use an alias name declared in the SELECT clause in your WHERE clause, because WHERE happens before SELECT.
Hence:
SELECT r.id, COALESCE(foo.url, bar.url) as url
FROM results r
LEFT JOIN foo USING (url_id)
LEFT JOIN bar USING (url_id)
WHERE COALESCE(foo.url, bar.url) IS NOT NULL;
or
SELECT r.id, COALESCE(foo.url, bar.url) as url
FROM results r
LEFT JOIN foo USING (url_id)
LEFT JOIN bar USING (url_id)
WHERE foo.url IS NOT NULL OR bar.url IS NOT NULL;
The direct answer to your question is to use table aliases when referring to columns. I would write this as a series of left joins:
SELECT
r.id,
r.url_id,
COALESCE(f.url, b.url) AS url
FROM results r
LEFT JOIN foo f
ON r.url_id = f.url_id
LEFT JOIN bar b
ON r.url_id = b.url_id;
This answer assumes that the actual url match could be in either table, or, if it matches to both, then you don't mind taking the version from the foo table.
Your error suggests to use table alias if same column name available with more than one table :
select f.id, ifnull(b.url, f.url) as url
from foo f left join
bar b
on b.id = f.id
where f.url_id = ?;
This answers your -> Column 'url' in where clause is ambiguous error, JOINs may not same as you want if so then adjust the on clause or provide one other conditions with JOIN. But the idea would be same to use alias to make it easier to understand the query or to compiler.
if you want url on where you can use sub-query cause your both table has url column but you make one so either you have to use table.column name in where
select * from
(
SELECT r.id,
COALESCE(foo.url, bar.url) as url
FROM results r
LEFT JOIN foo USING (url_id)
LEFT JOIN bar USING (url_id)
)as t where t.url is NOT NULL
So you have
SELECT ..., IFNULL(foo.url, bar.url) AS url
...
WHERE url LIKE ? *** Error: "url" ambiguous
The sorry solution is to use:
SELECT ..., IFNULL(foo.url, bar.url) AS url
...
WHERE IFNULL(foo.url, bar.url) LIKE ?
It should not be less efficient. It is what one pays.
Maybe the following would work:
SELECT ..., z.url
FROM (SELECT IFNULL(foo.url, bar.url) AS url FROM DUAL AS z)
JOIN ...
WHERE z.url LIKE ?
Introducing a table alias for disambiguation.
I have a tag field for a blog posts. tags have unique id but their displayName might be duplicated. What I want is a query that selects posts and in all_tags field we get couples of (id,displayName) is this way:
id1,name1;id2,name2;id3,name3
My query looks like:
select ....
CONCAT_WS(';', DISTINCT (CONCAT_WS(',',tags.id,tags.displayName))) AS all_tags
Join ...post content ...
Join ...post_tags ...
Join ...tags ...
ORDER BY posts.id
This line causes problem:
CONCAT_WS(';', DISTINCT (CONCAT_WS(',',tags.id,tags.displayName))) AS all_tags
How should I modify it?
Some people use an inner (SELECT .. FROM) but as I have heard, it is so inefficien
SELECT `posts`.*,`categories`.*,`creators`.*,`editors`.*
CONCAT_WS(';', DISTINCT GROUP_CONCAT(CONCAT_WS(',',tags.id,tags.displayName))) AS all_ids
FROM (`posts`)
LEFT JOIN `languages` ON `posts`.`language_id`=`languages`.`id`
LEFT JOIN `users` as creators ON `posts`.`creatorUser_id`=`creators`.`id`
LEFT JOIN `users` as editors ON `posts`.`lastEditorUser_id`=`editors`.`id`
LEFT JOIN `userProfiles` as editors_profile ON `editors`.`profile_id`=`editors_profile`.`id`
LEFT JOIN `categories` ON `posts`.`category_id`=`categories`.`id`
LEFT JOIN `postTags` ON `postTags`.`post_id`=`posts`.`id`
LEFT JOIN `tags` ON `postTags`.`tag_id`=`tags`.`id`
LEFT JOIN `postTags` as `nodetag_checks` ON `nodetag_checks`.`post_id`=`posts`.`id`
LEFT JOIN `tags` as `tag_checks` ON `nodetag_checks`.`tag_id`=`tag_checks`.`id`
WHERE ( 9 IN(`tag_checks`.`id`,`tag_checks`.`cached_parents`) OR 10 IN(`tag_checks`.`id`,`tag_checks`.`cached_parents`) OR 11 IN(`tag_checks`.`id`,`tag_checks`.`cached_parents`))
GROUP BY `posts`.`id` ORDER BY `posts`.`created` desc LIMIT 0, 20
Try this:
GROUP_CONCAT(
DISTINCT CONCAT(tags.id,',',tags.displayName)
ORDER BY posts.id
SEPARATOR ';'
)
As advised by #Willa, I add my comment as an anwser :
GROUP_CONCAT allows you to concat multiple fields :
GROUP_CONCAT(tags.id, ',', tags.displayName)
The only difference with Stephan's answer is in case your code allows the same tag to be affected several times to one post OR if you JOIN sequence leads you to multiselect the same record in the tag table. In those case, my solution will return the same tags multiple times.
On top of #Stephan's great answer, to prevent the same content showing up multiple times due to multiple JOIN's in your query but you don't want the id to show in the output...
GROUP_CONCAT(
DISTINCT
tags.displayName,
'||__', tags.id, '__||'
SEPARATOR '\n'
)
And then loop over the result in the end removing everything between ||__ and __|| .
This example is for php:
$data = preg_replace("/\|\|__.*__\|\|/", '', $data);
I want to create a simple search for a site that I'm working on. I have items in my db that all hold a specific category id and can optionally be linked to multiple tags.
I would like to take whatever search terms come in and query the category.name and tag.name fields to find the items that match those terms.
I'm looking for advice on how to create an efficient/quick query that does this AND orders the results by the items that match closest(most matches)
Here's a quick version of my relevant tables:
item
id | category | title | description
category
id | name | parentId
tag
id | name | uses
item_tag
itemId | tagId
I still didn't entirely understand what you want.
Well, here's a first version for us to discuss.
I suggest you to create a view as the following:
CREATE OR REPLACE VIEW `search_view` AS
SELECT
i.id AS `item_id`,
i.title AS `item_title`,
c.id AS `cat_id`,
c.name AS `cat_name`,
t.id AS `tag_id`,
t.name AS `tag_name`
FROM item AS i
LEFT OUTER JOIN item_tag AS it
ON (i.id = it.itemId)
LEFT OUTER JOIN tag AS t
ON (it.tagId = t.id)
LEFT OUTER JOIN category AS c
ON (i.category = c.id)
WHERE ((t.id IS NOT NULL) OR (c.id IS NOT NULL));
And then you query from the view using something near
SELECT
*,
(
IF(tag_name like ?, 2, 0)
+ IF(cat_name like ?, 4, 0)
+ IF(item_title like ?, 1, 0)
) AS `priority`
FROM search_view
GROUP BY item_id
ORDER BY SUM(priority);
No tests in the code above. Report any problems you have
[first edition] You use PHP, don't you?
Well, you can normalize your queries using PHP string functions; one way is to replace every occurrence of ',' by '|', remove extra spaces and perform the following query: [I'll give an example using #VAR (actually you'll replace it with your input string)]
SET #VAR = 'notebook|samsung';
SELECT
*,
(
IF (tag_name REGEXP CONCAT('.*(', #VAR, ').*'), 2, 0)
+ IF (cat_name REGEXP CONCAT('.*(', #VAR, ').*'), 4, 0)
+ IF (item_title REGEXP CONCAT('.*(', #VAR, ').*'), 1, 0)
) AS `priority`
FROM search_view
ORDER BY priority DESC;
This time I tested. Yes, you can use MySQL functions, something about REPLACE(REPLACE(#VAR,' ',''), ',', '|'). But I recommend you to do it in PHP (or java, python etc).
SELECT item.*
FROM items
LEFT JOIN categories
ON categories.name = '< input name >'
AND categories.id = items.category
LEFT JOIN item_tags
AND item_tags.itemId = items.id
LEFT JOIN tags
ON tags.name = '< input name >'
AND tags.id = item_tags.tagId
WHERE categories.id IS NOT NULL
OR tags.id IS NOT NULL
ORDER BY COUNT(items.id) DESC;
This may not be the fastest way. Basically you'll left join categories and tags to items while making sure the category and tag have the correct name. Filter out all items that don't match a category or an item_tag in the where clause.
Another alternative would be to create temporary tables. Create one for all categories with the correct name, and one with all tags with the correct name. You could then SELECT items WHERE items.id IN categories_table OR items.id IN tags_table
I whant to do something like this:
SELECT
s.name,
COUNT(r.request_log_id) AS NumberOfRequests
FROM
station AS s
LEFT JOIN request_log AS r ON r.requested LIKE '%s.station_id%'
GROUP BY 1
But this gives 0 in the NumberOfRequests and i think it is becouse its hardcoded s.station_id its running LIKE on.
Does anyone know how i can do this?
For your information, r.requested is a string whit some data in it and one example is
/StationInfo - stationID = "262" - Status = 0
So it is the staitonid i whant to get LIKE on and cound how many rows in request_log table it is in that have the station id for the first table named station
First approach:
SELECT
s.name,
COUNT(r.request_log_id) AS NumberOfRequests
FROM
station AS s
LEFT JOIN request_log AS r
ON r.requested LIKE concat( '%',s.station_id, '%')
GROUP BY 1
be careful with this non equijoin.
see you.
I have a query like this:
SELECT product.id,
GROUP_CONCAT(image.id) AS images_id,
GROUP_CONCAT(image.title) AS images_title,
GROUP_CONCAT(facet.id) AS facets_id
...
GROUP BY product.id
And the query works, but not as expected, because if I have a product with 5 facets and 1 image (suppose an image with id=7), then I get something like this in "images_id":
"7,7,7,7,7"
If I have 2 images (7 and 3) then I get something like:
"7,7,7,7,7,3,3,3,3,3"
and in facets I get something like:
"8,7,6,5,4,8,7,6,5,4"
I think MySQL is making some type of union of the differents rows returned by the query, and then concatenating everything.
My expected result is (for the last example):
images_id = "7,3"
facets_id = "8,7,6,5,4"
I can obtain that using DISTINCT in the GROUP_CONCAT, but then I have another problem:
If I have two images with the same title, one of them is ommited, and then I get something like:
images_id = "7,3,5"
images_title = "Title7and3,Title5"
So I miss the relation between images_id and images_title.
Does somebody know if it's possible to make this query in MySQL?
Maybe I'm complicating everything without any real benefits.
I'm trying to execute only one query because performance, but now I'm not so sure if it's even faster to execute two queries (one for selecting the facets and another for the images for example).
Please explain what do you think is the best solution for this and why.
Thanks !
Just add DISTINCT.
Example:
GROUP_CONCAT(DISTINCT image.id) AS images_id
You'll need to get each group separately:
SELECT
p.id,
images_id,
images_title,
facets_id,
...
FROM PRODUCT p
JOIN (SELECT product.id, GROUP_CONCAT(image.id) AS images_id
FROM PRODUCT GROUP BY product.id) a on a.id = p.id
JOIN (SELECT product.id, GROUP_CONCAT(image.title) AS images_title
FROM PRODUCT GROUP BY product.id) b on b.id = p.id
JOIN (SELECT product.id, GROUP_CONCAT(facet.id) AS facets_id
FROM PRODUCT GROUP BY product.id) b on c.id = p.id
...
You can add just the DISTINCT keyword, you'll get your desire results.
SELECT tb_mod.*, tb_van.*,
GROUP_CONCAT(DISTINCT tb_voil.vt_id) AS voil,
GROUP_CONCAT(DISTINCT tb_other.oa_id) AS other,
GROUP_CONCAT(DISTINCT tb_ref.rp_id) AS referral
FROM cp_modules_record_tbl tb_mod
LEFT JOIN cp_vane_police_tbl tb_van ON tb_van.mr_id= tb_mod.id
LEFT JOIN cp_mod_voilt_tbl tb_voil ON tb_voil.mr_id= tb_mod.id
LEFT JOIN cp_mod_otheraction_tbl tb_other ON tb_other.mr_id= tb_mod.id
LEFT JOIN cp_mod_referral_tbl tb_ref ON tb_ref.mr_id= tb_mod.id
WHERE tb_mod.mod_type = 2 GROUP BY tb_mod.id
If the issue is speed, then it may be a lot faster to simply select all the data you need as separate rows, and do the grouping in the application, i.e.:
SELECT product.id, image.id, image.title, facet.id
Then in the application:
foreach row:
push product_id onto list_of_product_ids
push image_id onto list_of_image_ids
etc.