I have the following table:
id | name | description1 | description2
--------------------------------------------------
1 | bob | he is tall. | he is smart.
2 | bob | he is fat. | he is big.
3 | john | he is white. | he is also tall.
4 | brian | he is asian | he is the tallest.
5 | john | he is second. | he is dumb.
I want to combine the two columns, but then want to combine the rows based the names, so it ends up like this:
id | name | combined_description
--------------------------------------------------
1 | bob | he is tall. he is smart. he is fat. he is big.
3 | john | he is white. he is also tall. he is second. he is dumb.
4 | brian | he is asian. he is the tallest.
I know combining columns "description1" and "description2" is easy enough, but how would I combine rows based on having the same name?
select
min(id) as id,
name,
group_concat(description1, ' ', description2 order by id separator ' ') as combined_description
from your_table
group by name;
But group_concat will truncate the results at ##group_concat_max_len characters, which is often 1024. So you may need to set that to a higher value before doing the query.
Related
I have two databases, one database containing all my pictures like:
Database 1 name: images
-----+----------------------------+----------------------+-----------+------------+------------
| id | description | tags | imagename | resolution | location |
-----+----------------------------+----------------------+-----------+------------+------------
| 1 | We standing at eiffeltower | france, green, blue | IMG01.JPG | 1280x1024 | /img/2020 |
| 2 | We standing at bridge | france, orange, grey | IMG02.JPG | 1280x1024 | /img/2020 |
Database 2 name tagTranslations (for Dutch translation)
-----+--------+-----------------------
| id | tag | translation |
-----+--------+-----------------------
| 1 | france | frankrijk |
| 2 | orange | oranje. |
| 3 | grey | grijs. |
| 4 | green | groen |
| 5 | blue | blauw |
Now i want with 1 mysql query to get a result like this:
"We standing at eiffeltower", "france, green, blue", "IMG01.JPG", "1280x1024", "/img/2020", "frankrijk", "groen", "blauw"
"We standing at bridge", "france, orange, grey", "IMG02.JPG", "1280x1024", "/img/2020", "frankrijk", "oranje", "grijs"
You first effort should go into fixing your data modeL Each image tag should be stored in a separate table, on a different row. Storing delimited lists in database columns in the root of many evils, as you are starting to see. More about this can be read in this famous SO post.
That said, you could use a corelated subquery with find_in_set() and group_concat():
select
i.id,
i.description,
(
select group_concat(
tt.translation
order by find_in_set(tt.tag, replace(i.tags, ', ', ','))
separator ', '
)
from tagTranslations tt
where find_in_set(tt.tag, replace(i.tags, ', ', ','))
) tags,
i.imagename,
i.resolution,
i.location
from images i
The correlated subquery retrieves rows from the translation table whose tag can be found in the tags list of the corresponding images row. For this, we use handly MySQL function find_in_set() (we need to remove the space after the comma for the function to work properly); then, aggregation function group_concat() regenerates a delimited list of translations, using find_in_set() again to honor the original order of tags.
Demo on DB Fiddle:
id | description | tags | imagename | resolution | location
-: | :------------------------- | :----------------------- | :-------- | :--------- | :--------
1 | We standing at eiffeltower | frankrijk, groen, blauw | IMG01.JPG | 1280x1024 | /img/2020
2 | We standing at bridge | frankrijk, oranje, grijs | IMG02.JPG | 1280x1024 | /img/2020
Try out the code below:
CREATE VIEW table_comb AS
SELECT * FROM images
UNION ALL
SELECT * FROM tagTranslations
For example, there are three rooms.
1|gold_room|1,2,3
2|silver_room|1,2,3
3|brown_room|2,4,6
4|brown_room|3
5|gold_room|4,5,6
Then, I'd like to get
gold_room|1,2,3,4,5,6
brown_room|2,3,4,6
silver_room|1,2,3
How can I achieve this?
I've tried: select * from room group by name; And it only prints the first row. And I know CONCAT() can combine two string values.
Please use below query,
select col2, GROUP_CONCAT(col3) from data group by col2;
Below is the Test case,
https://dbfiddle.uk/?rdbms=mysql_8.0&fiddle=ab35e8d66ffe3ac6436c17faf97ee9af
I'm not making an assumption that the lists don't have elements in common on separate rows.
First create a table of integers.
mysql> create table n (n int primary key);
mysql> insert into n values (1),(2),(3),(4),(5),(6);
You can join this to your rooms table using the FIND_IN_SET() function. Note that this cannot be optimized. It will execute N full table scans. But it does create an interim set of rows.
mysql> select * from n inner join rooms on find_in_set(n.n, rooms.csv) order by rooms.room, n.n;
+---+----+-------------+-------+
| n | id | room | csv |
+---+----+-------------+-------+
| 2 | 3 | brown_room | 2,4,6 |
| 3 | 4 | brown_room | 3 |
| 4 | 3 | brown_room | 2,4,6 |
| 6 | 3 | brown_room | 2,4,6 |
| 1 | 1 | gold_room | 1,2,3 |
| 2 | 1 | gold_room | 1,2,3 |
| 3 | 1 | gold_room | 1,2,3 |
| 4 | 5 | gold_room | 4,5,6 |
| 5 | 5 | gold_room | 4,5,6 |
| 6 | 5 | gold_room | 4,5,6 |
| 1 | 2 | silver_room | 1,2,3 |
| 2 | 2 | silver_room | 1,2,3 |
| 3 | 2 | silver_room | 1,2,3 |
+---+----+-------------+-------+
Use GROUP BY to reduce these rows to one row per room. Use GROUP_CONCAT() to put the integers together into a comma-separated list.
mysql> select room, group_concat(distinct n.n order by n.n) as csv
from n inner join rooms on find_in_set(n.n, rooms.csv) group by rooms.room
+-------------+-------------+
| room | csv |
+-------------+-------------+
| brown_room | 2,3,4,6 |
| gold_room | 1,2,3,4,5,6 |
| silver_room | 1,2,3 |
+-------------+-------------+
I think this is a lot of work, and impossible to optimize. I don't recommend it.
The problem is that you are storing comma-separated lists of numbers, and then you want to query it as if the elements in the list are discrete values. This is a problem for SQL.
It would be much better if you did not store your numbers in a comma-separated list. Store multiple rows per room, with one number per row. You can run a wider variety of queries if you do this, and it will be more flexible.
For example, the query you asked about, to produce a result with numbers in a comma-separated list is more simple, and you don't need the extra n table:
select room, group_concat(n order by n) as csv from rooms group by room
See also my answer to Is storing a delimited list in a database column really that bad?
I have the following situation:
Table Words:
| ID | WORD |
|----|--------|
| 1 | us |
| 2 | to |
| 3 | belong |
| 4 | are |
| 5 | base |
| 6 | your |
| 7 | all |
| 8 | is |
| 9 | yours |
Table Sentence:
| ID | SENTENCE |
|----|-------------------------------------------|
| 1 | <<7>> <<6>> <<5>> <<4>> <<3>> <<2>> <<1>> |
| 2 | <<7>> <<8>> <<9>> |
And i want to replace the <<(\d)>> with the equivalent word from the Word-Table.
So the result should be
| ID | SENTENCE |
|----|--------------------------------|
| 1 | all your base are belong to us |
| 2 | all is yours |
What i came up with is the following SQL-Code:
SELECT id, GROUP_CONCAT(word ORDER BY pos SEPARATOR ' ') AS sentence FROM (
SELECT sentence.id, words.word, LOCATE(words.id, sentence.sentence) AS pos
FROM sentence
LEFT JOIN words
ON (sentence.sentence REGEXP CONCAT('<<',words.id,'>>'))
) AS TEMP
GROUP BY id
I made a sqlfiddle for this:
http://sqlfiddle.com/#!2/634b8/4
The code basically is working, but i'd like to ask you pros if there is a way without a derived table or without filesort in the execution plan.
You should make a table with one entry per word, so your sentense (sic) can be made by joining on that table. It would look something like this
SentenceId, wordId, location
2, 7, 1
2, 8, 2
2, 9, 3
They way you have it set up, you are not taking advantage of your database, basically putting several points of data in 1 table-field.
The location field (it is tempting to call it "order", but as this is an SQL keyword, don't do it, you'll hate yourself) can be used to 'sort' the sentence.
(and you might want to rename sentense to sentence?)
I'm having MySQL problems and I'm in need of a beer lunch. I wanted to do a query like this:
SELECT MATCH(some_string) AGAINST ('beer lunch') FROM (SELECT GROUP_CONCAT(some_column) AS some_string FROM myrealtable) AS mytablealias;
Unfortunately I discovered I cannot do a FULLTEXT MATCH against a GROUP_CONCAT column since the FULLTEXT index exists for the original column (some_column) only and not in the concatenated column (some_string) in the alias table.
I really need to do a FULLTEXT search and generate a relevance score for concatenated strings that are broken across multiple rows in my table.
Here's a little thought experiment that I put together to study the relevance problem. Let's start with a table that has the concatenated strings:
+----------+-------------------------------------------------------------------------------------+
| table_id | concat_string |
+----------+-------------------------------------------------------------------------------------+
| 1 | I like beer Beer is a healthy choice My brother drinks beer for lunch every day |
| 2 | I like juice Juice is a healthier choice My brother drinks beer for lunch every day |
+----------+-------------------------------------------------------------------------------------+
Now I do the following MATCH query on this table: SELECT table_id,MATCH(concat_string) AGAINST('beer lunch') AS score FROM myconcattable; and I get the following relevance scores:
+----------+----------------------------+
| table_id | score |
+----------+----------------------------+
| 1 | 0.000000007543713209656744 |
| 2 | 0.000000003771856604828372 |
+----------+----------------------------+
Clearly the first row is far more relevant than the second when searching for "beer lunch" ... but the problem is that my strings are broken across multiple rows that need to be grouped according to a foreign key (foreign_id). Here's what my table really looks like:
+----------+--------------------------------------------+------------+
| table_id | some_string | foreign_id |
+----------+--------------------------------------------+------------+
| 1 | I like beer | 1 |
| 2 | Beer is a healthy choice | 1 |
| 3 | My brother drinks beer for lunch every day | 1 |
| 4 | I like juice | 2 |
| 5 | Juice is a healthier choice | 2 |
| 6 | My brother drinks beer for lunch every day | 2 |
+----------+--------------------------------------------+------------+
So now let's try the query (SELECT table_id,MATCH(some_string) AGAINST('beer lunch') AS score, foreign_id FROM mybrokentable;) on this table:
+----------+----------------------+------------+
| table_id | score | foreign_id |
+----------+----------------------+------------+
| 1 | 0.031008131802082062 | 1 |
| 2 | 0.031008131802082062 | 1 |
| 3 | 0.25865283608436584 | 1 |
| 4 | 0 | 2 |
| 5 | 0 | 2 |
| 6 | 0.25865283608436584 | 2 |
+----------+----------------------+------------+
OK, so if I add up the scores as a sum, then foreign_id 1 looks a bit more relevant than foreign_id 2, but it is not very accurate compared to when the strings were concatenated into one table.
Ideally, I'd like to devise a query that would generate a relevance score for the foreign ids like this:
+----------------------------+------------+
| score | foreign_id |
+----------------------------+------------+
| 0.000000007543713209656744 | 1 |
| 0.000000003771856604828372 | 2 |
+----------------------------+------------+
Any ideas of what I should do?
You could try summing the scores in your individual phrase matches, like so, with a subquery. You'll have to experiment to see if it gives you the result you need.
SELECT SUM(score) AS total_score,
foreign_id
FROM (
SELECT table_id,
MATCH(some_string) AGAINST('beer lunch') AS score,
foreign_id
FROM phrase
) AS scores
GROUP BY foreign_id
ORDER BY total_score DESC
I used BOOLEAN MODE in this fiddle http://sqlfiddle.com/#!2/355b1/1/0 because ordinary fulltext search does odd things with stopwords on small samples of text.
Some background: an 'image' is part of one 'photoshoot', and may be a part of zero or many 'galleries'. My tables:
'shoots' table:
+----+--------------+
| id | name |
+----+--------------+
| 1 | Test shoot |
| 2 | Another test |
| 3 | Final test |
+----+--------------+
'images' table:
+----+-------------------+------------------+
| id | original_filename | storage_location |
+----+-------------------+------------------+
| 1 | test.jpg | store/test.jpg |
| 2 | test.jpg | store/test.jpg |
| 3 | test.jpg | store/test.jpg |
+----+-------------------+------------------+
'shoot_images' table:
+----------+----------+
| shoot_id | image_id |
+----------+----------+
| 1 | 1 |
| 1 | 2 |
| 3 | 3 |
+----------+----------+
'gallery_images' table:
+------------+----------+
| gallery_id | image_id |
+------------+----------+
| 1 | 1 |
| 1 | 2 |
| 2 | 3 |
| 3 | 1 |
| 4 | 1 |
+------------+----------+
What I'd like to get back, so I can say 'For this photoshoot, there are X images in total, and these images are featured in Y galleries:
+----+--------------+-------------+---------------+
| id | name | image_count | gallery_count |
+----+--------------+-------------+---------------+
| 3 | Final test | 1 | 1 |
| 2 | Another test | 0 | 0 |
| 1 | Test shoot | 2 | 4 |
+----+--------------+-------------+---------------+
I'm currently trying the SQL below, which appears to work correctly but only ever returns one row. I can't work out why this is happening. Curiously, the below also returns a row even when 'shoots' is empty.
SELECT shoots.id,
shoots.name,
COUNT(DISTINCT shoot_images.image_id) AS image_count,
COUNT(DISTINCT gallery_images.gallery_id) AS gallery_count
FROM shoots
LEFT JOIN shoot_images ON shoots.id=shoot_images.shoot_id
LEFT JOIN gallery_images ON shoot_images.image_id=gallery_images.image_id
ORDER BY shoots.id DESC
Thanks for taking the time to look at this :)
You are missing the GROUP BY clause:
SELECT
shoots.id,
shoots.name,
COUNT(DISTINCT shoot_images.image_id) AS image_count,
COUNT(DISTINCT gallery_images.gallery_id) AS gallery_count
FROM shoots
LEFT JOIN shoot_images ON shoots.id=shoot_images.shoot_id
LEFT JOIN gallery_images ON shoot_images.image_id=gallery_images.image_id
GROUP BY 1, 2 -- Added this line
ORDER BY shoots.id DESC
Note: The SQL standard allows GROUP BY to be given either column names or column numbers, so GROUP BY 1, 2 is equivalent to GROUP BY shoots.id, shoots.name in this case. There are many who consider this "bad coding practice" and advocate always using the column names, but I find it makes the code a lot more readable and maintainable and I've been writing SQL since before many users on this site were born, and it's never cause me a problem using this syntax.
FYI, the reason you were getting one row before, and not getting and error, is that in mysql, unlike any other database I know, you are allowed to omit the group by clause when using aggregating functions. In such cases, instead of throwing a syntax exception, mysql returns the first row for each unique combination of non-aggregate columns.
Although at first this may seem abhorrent to SQL purists, it can be incredibly handy!
You should look into the MySQL function group by.