SQL combining Rows in Query - mysql

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.

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

SQL where particular column values appears

I wasn't sure how to really search for this..
Lets say I have a simple table like this
ID Type
1 0
1 1
2 1
3 0
4 0
4 1
How could I select all ID's which have a type of both 0 and 1?
SELECT id,type
FROM t
GROUP BY id
HAVING SUM(type=0)>0
AND SUM(type=1)>0
You just group by id ,than with HAVING you use post aggregation filtering to check for 0 and 1.
Having is pretty expensive and that query can't hit keys.
SELECT ID FROM foo AS foo0 JOIN foo AS foo1 USING (ID) WHERE foo0.Type=0 AND foo1.Type=1 GROUP BY foo0.id.
A more generalized way of doing this would by to use a CASE column for each value you need to test combined with a GROUP BY on the id column. This means that if you have n conditions to test for, you would have a column indicating if each condition is met for a given id. Then the HAVING condition becomes trivial and you can use it like any multi-column filter, or use the grouping as your subquery and the code looks simpler and the logic becomes even easier to follow.
SELECT id, Type0,Type1
FROM (
SELECT id,
Type0 = max(CASE WHEN type = 0 THEN TRUE END)
, Type1 = max(CASE WHEN type = 1 THEN TRUE END)
FROM t
GROUP BY id
) pivot
WHERE Type0 = TRUE and Type1 = TRUE

Mysql SELECT where 2 column match set of values

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);

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.

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