Mysql SELECT where 2 column match set of values - mysql

I am doing product filter the point is the more specific the user select the products the less results should appear. At he moment I am writing multiple queries and storing in arrays and checking for array intersect, but the result is opposite, which means when user apply more filters, i will show more products.
So i am thinking there could be a SQL command which I don't know!
simplified example:
------------
table "filter"
------------
product
Spec
value
------------
Sample data
------------
book1,page,200
book1,cover,leather
book1,language,en
book2,page,300
book2,cover,paper
book2,language,de
book3,page,150
book3,cover,hard
book3,language,en
SELECT `product` FROM `filter` where ...
how do I select (page=200 and langauge=en)?

If understand correctly you are probably looking for something like this
SELECT product
FROM filter
WHERE (spec = 'page' AND value = '200')
OR (spec = 'language' AND value = 'en')
GROUP BY product
HAVING COUNT(*) = 2 -- 2 here represents number of spec-value pairs
Output:
| PRODUCT |
-----------
| book1 |
SQLFiddle

Another alternative, but less elegant. I just wanted to show another way of doing it.
SELECT DISTINCT product
FROM filter f
WHERE
EXISTS (SELECT 1 FROM filter WHERE spec = 'language' AND value = 'en' AND product = f.product)
AND EXISTS (SELECT 1 FROM filter WHERE spec = 'page' AND value = 200 AND product = f.product);

Related

Append string to a record's specified column after SELECT command

I have an sql command that returns me a list of duplicated items (in my MySQL database), only two columns, one for the duplicated value and one for the count of duplicated records.
SELECT title, COUNT(*) c FROM posts GROUP BY title HAVING c > 1
title c
---------------
title_1 2
title_a 2
title_b 2
I assume one result looks like this:(and it's an array of arrays)
objId title
------------
1 title_1
2 title_1
So my goal is to append a string to the second item of a result in the array of the duplicated record's like this:
objId title
------------
1 title_1
2 title_1_2
I've found a solution to update the record, but I don't have an idea how could I loop through the results that I get after the first sql command so I can't utilize it in practice.
UPDATE posts SET title = CONCAT(IFNULL(title,''), ' 2');
In pseudo code I would do something like this to create the new string for the title:
result[1].title = (oldTitleString," 2");
save result[1];
I'm new in sql and don't really know about the possibilities, maybe there would be an easier way to do it, so I would really appreciate if somebody could show me how can I get the second record from the duplicated item and extend it with another string.
My solution:
SELECT `objId`,`title`,
(SELECT CONCAT(`title`, '_', `po`.`objId`)
FROM `posts` `p`
WHERE `title` = `po`.`title` && `p`.`objId` < `po`.`objId` LIMIT 1) AS `title_custom`
FROM `posts` `po`
Here is sample fiddle:
http://sqlfiddle.com/#!9/a4164/8
Query looks like this:
select id, title,
concat(title,'_',
(select count(*) from posts p2 where p2.title = p1.title and p2.id <= p1.id)),
title,
count(*) c
from posts p1
group by title
having c > 1

Dynamic Table Joining Based on Another Table Column Value?

I'm trying to figure out if there is a simple way to dynamically load a 2nd table based on the column value of the first table with mysql
Servers (Table 1):
ID | Game | Title
Servers_1 (Table 2, option 1):
server_id (links to servers.id) | game_version | players | plugins
Servers_2 (Table 2, option 2):
server_id (links to servers.id) | game_version | players | mods | game_map
Servers_etc. (Table 2, option etc.)
Trying to figure out how to do something like
left_join servers_[servers.game] on servers.id = servers_[servers.game].server_id
So it would grab the value of servers.game and use that to finish the table name. If this is not possible, then is a case statement possible such as:
Left_Join
if ( servers.game == 1 ) 'servers_1'
elseif ( servers.game == 2 ) 'servers_2'
elseif ( servers.game == 3 ) 'servers_3'
One option would be to LEFT JOIN each of the tables and use a CASE statement to return the appropriate data.
Something like this should help get you started:
SELECT S.Id, S.Game, S.Title,
CASE S.Game
WHEN 1 THEN S1.game_version
WHEN 2 THEN S2.game_version
END game_version,
...
FROM Servers S
LEFT JOIN Servers_1 S1 ON S.id = S1.Server_Id AND S.Game = 1
LEFT JOIN Servers_2 S2 ON S.id = S2.Server_Id AND S.Game = 2
Instead of using CASE, you could probably just use COALESCE as each Id/Game should be unique and only 1 wouldn't be NULL:
SELECT COALESCE(S1.game_version,S2.game_version,...) game_version
If there is no way the same server id can be in multiple tables, then you can leave the AND S.Game... out of the LEFT JOINs as it wouldn't longer be needed. Depends on your unique keys.
Alternatively, you could use Dynamic SQL.

SQL combining Rows in Query

I am using a MySQL DB and I'm trying to combine my results. my SQL Chops aren't experienced enough to come up with a way to accomplish this.
the Picture explains my objective and db structure. http://i.stack.imgur.com/x6dCo.png
this is what I was Using:
SELECT
LISTITEMS.ID,
LISTITEMS.ItemName,
PROPVALUE.PropValue,
LISTPROPERTIES.PropName
FROM LISTITEMS
INNER JOIN PROPVALUE
ON LISTITEMS.ID = PROPVALUE.ItemID
INNER JOIN LISTPROPERTIES
ON LISTPROPERTIES.ID = PROPVALUE.PropID
WHERE LISTITEMS.ListID = '$_GET[ID]'
this Returns 2 rows and I need 1 row (I edited out the 'ID' columns from the other 2 tables).
ID ItemName PropValue ListID PropName
1 item 1 PropValue1 1 Property1
1 item 1 PropValue2 1 Property2
Edit Desired output:
ID ListID ItemName PropName1 PropValue1 PropName2 PropValue2
1 1 Item 1 Property1 PropValue1 Property2 PropValue2
Since the property names will differ dynamically, it is going to be far easier to combine them in a GROUP_CONCAT() and then separate them in your application layer:
Edit: Fixed query to include all your JOINs:
SELECT
LISTITEMS.ID,
LISTITEMS.ItemName,
GROUP_CONCAT(CONCAT_WS('|', LISTPROPERTIES.PropName, PROPVALUE.PropValue)) AS Properties
FROM LISTITEMS
INNER JOIN PROPVALUE
ON LISTITEMS.ID = PROPVALUE.ItemID
INNER JOIN LISTPROPERTIES
ON LISTPROPERTIES.ID = PROPVALUE.PropID
WHERE LISTITEMS.ListID = '$_GET[ID]'
GROUP BY LISTITEMS.ID, LISTITEMS.ItemName
This will output something like:
ID ItemName Properties
1 Item1 Property1|PropValue1,Property2|PropValue2,Property3|PropValue3
Then in your PHP code, explode() the properties first on , to get the pairs, then explode() each of those on | to get the name|value key & value.

How to use result of an subquery multiple times into an query

A MySQL query needs the results of a subquery in different places, like this:
SELECT COUNT(*),(SELECT hash FROM sets WHERE ID=1)
FROM sets
WHERE hash=(SELECT hash FROM sets WHERE ID=1)
and XD=2;
Is there a way to avoid the double execution of the subquery (SELECT hash FROM sets WHERE ID=1)?
The result of the subquery always returns an valid hash value.
It is important that the result of the main query also includes the HASH.
First I tried a JOIN like this:
SELECT COUNT(*), m.hash FROM sets s INNER JOIN sets AS m
WHERE s.hash=m.hash AND id=1 AND xd=2;
If XD=2 doesn't match a row, the result is:
+----------+------+
| count(*) | HASH |
+----------+------+
| 0 | NULL |
+----------+------+
Instead of something like (what I need):
+----------+------+
| count(*) | HASH |
+----------+------+
| 0 | 8115e|
+----------+------+
Any ideas? Please let me know! Thank you in advance for any help.
//Edit:
finally that query only has to count all the entries in an table which has the same hash value like the entry with ID=1 and where XD=2. If no rows matches that (this case happend if XD is set to an other number), so return 0 and simply hash value.
SELECT SUM(xd = 2), hash
FROM sets
WHERE id = 1
If id is a PRIMARY KEY (which I assume it is since your are using a single-record query against it), then you can just drop the SUM:
SELECT xd = 2 AS cnt, hash
FROM sets
WHERE id = 1
Update:
Sorry, got your task wrong.
Try this:
SELECT si.hash, COUNT(so.hash)
FROM sets si
LEFT JOIN
sets so
ON so.hash = si.hash
AND so.xd = 2
WHERE si.id = 1
I normally nest the statements like the following
SELECT Count(ResultA.Hash2) AS Hash2Count,
ResultA.Hash1
FROM (SELECT S.Hash AS Hash2,
(SELECT s2.hash
FROM sets AS s2
WHERE s2.ID = 1) AS Hash1
FROM sets AS S
WHERE S.XD = 2) AS ResultA
WHERE ResultA.Hash2 = ResultA.Hash1
GROUP BY ResultA.Hash1
(this one is hand typed and not tested but you should get the point)
Hash1 is your subquery, once its nested, you can reference it by its alias in the outer query. It makes the query a little larger but I don't see that as a biggy.
If I understand correctly what you are trying to get, query should look like this:
select count(case xd when 2 then 1 else null end case), hash from sets where id = 1 group by hash
I agree with the other answers, that the GROUP BY may be better, but to answer the question as posed, here's how to eliminate the repetition:
SELECT COUNT(*), h.hash
FROM sets, (SELECT hash FROM sets WHERE ID=1) h
WHERE sets.hash=h.hash
and sets.ID=1 and sets.XD=2;

Mysql multiple tables select

I've got a table, called for example, "node", from which I need to return values for as shown:
SELECT nid FROM node WHERE type = "book"
After I get a list of values let's say:
|**nid**|
|123|
|12451|
|562|
|536|
Then I need to take these values, and check another table, for rows where column 'path' has values as "node/123", "node/12451" (numbers the previous request returned) in one joined request. It all would be easier if collumn 'path' had simple numbers, without the 'node/'.
And then also count the number of identical i.e. 'node/123' returned.
End result would look like:
nid | path | count(path) | count(distinct path)
123 |node/123| 412 | 123
562 |node/562| 123 | 56
Works fine if done in multiple separated queries, but that won't do.
select a.nid from node a join othertable b
on b.path = concat("node/", a.nid) where type='book'
You can probably do something like the following (nid may require additional conversion to some string type):
SELECT *
FROM OtherTable
JOIN node ON path = CONCAT('node/', nid)
WHERE type = 'book'
Thank you all for your help. Basically, the problem was that I didn't know how to get nid and node/ together, but concat helped.
End result looks something like:
SELECT node.nid, accesslog.path, count(accesslog.hostname), count(distinct accesslog.hostname)
FROM `node`, `accesslog`
WHERE node.uid=1
AND node.type='raamat'
AND accesslog.path = CONCAT('node/', node.nid)
GROUP BY node.nid