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
Related
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.
Basically I have two tables
MY_CHARACTER:
_________________________________________________
| char_ID | char_name | char_class | char_rank |
|------------------------------------------------|
| 1 | Aragorn | Fighter | 99 |
| 2 | Legolas | Archer | 90 |
| 3 | Smeagle | Spy | 20 |
|________________________________________________|
and
EQUIPMENT:
_________________________________________________
| equip_ID | equip_name | equip_owner | required |
|--------------------------------------------------|
| 1 | The one ring | 3 | 99 |
| 2 | Longsword | 1 | 90 |
| 3 | Waistcloth | 3 | 10 |
| 4 | Nazguls Mask | 2 | 95 |
|__________________________________________________|
Now what I want is to make a select command using IN condition, which will give me list of equipments that the characters are not eligible to use. So in this very example, it would be Smeagle being ineligible to wear the one ring and Legolas unable to wield the nazguls mask.
My command looks something like this
SELECT equip_name, equip_owner, required
FROM EQUIPMENT WHERE required IN (SELECT char_rank MY_CHARACTER);
Now this will only print out the equipment where required=char_rank however i want to print out a select, where required equipment level is higher than char_rank instead. Any idea how? Ideally using the IN condition.
EDIT: To clear out confusion regarding where I want, basically what my command does right now is it checks if EQUIPMENT.required=MY_CHARACTER.char_rank what I want instead is that it checks EQUIPMENT.required>MY_CHARACTER.char_rank
I would do this with a join:
select c.*, equip_id as notEligible
from my_character c join
equipment e
on c.char_rank < e.required;
So if you want a list of items, that a certain character is not allowed to use you can slightly modify your SQL:
SELECT
equip_name, equip_owner, required
FROM
EQUIPMENT
WHERE
required > (SELECT char_rank FROM MY_CHARACTER WHERE char_id=?);
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?)
What's the best way to perform a group by statement in a csv-like column using SQL (mySQL)?
Table Products Color
+-------------+--------------+
| Product Id | Color |
+-------------+--------------+
| 120 | Red, Blue |
| 121 | Green, Black |
| 122 | Red, Black |
+-------------+--------------+
From the table above I need to count how many times a color appears and return something like this:
+-------------+-------+
| Color | Count |
+-------------+-------+
| Red | 2 |
| Blue | 1 |
| Green | 1 |
| Black | 2 |
+-------------+-------+
Is possible to do this without normalize the database?
If you can't change your table structure, but you can create a table that lists all the colors, you could use this query:
SELECT Colors.Color, Count(*)
FROM
Products INNER JOIN Colors
ON FIND_IN_SET(Colors.Color, REPLACE(Products.Color, ' ', ''))>0
GROUP BY
Colors.Color
See it working here. Please notice that it can't be optimized because it can't make use on an index.
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.