how do I concatenate rows in a mysql query - mysql

table 1
--------
pid | name
1 someone
2 another
table2
--------
bid | valu
1 drum
2 guitar
3 flower
4 cake
table3
------
id | pid | bid | pref
1 1 3 yes
2 1 1 maybe
3 1 2 no
4 2 4 definately
5 2 2
6 2 3 no
So as you can see I have 3 simple tables where the third one is used to create a mapping between table 1 and 2 along with some additional data. Now I need to write a query to display the valu and pref in a concatenated string based on the pid,
So against pid = 1, the expected output is something like
flower yes, drum maybe, guitar no....so How do I write this query?
I tried( pretty much a blind guess):
SELECT opa.name, GROUP_CONCAT(CONCAT(opb.valu,' ',opc.pref) SEPARATOR ',') AS myChoice
From
table_1 opa
INNER JOIN table_3 opc ON opc.pid = opa.pid
INNER JOIN table_2 opb ON opb.bid = opc.bid
Any help is appreciated.

your query is right you just forgot the GROUP BY
SELECT opa.name, GROUP_CONCAT(CONCAT(opb.valu,' ',opc.pref) SEPARATOR ',') AS myChoice
From
table1 opa
INNER JOIN table3 opc ON opc.pid = opa.pid
INNER JOIN table2 opb ON opb.bid = opc.bid
group by opc.pid
DEMO HERE

Related

SQL Select parent as column name and child as value

I am creating a database to store music.
There are different categories that each have sub categories. Example:
id name parentID
1 instrumentation null
2 solo_instrument null
3 Concert Band 1
4 Brass Band 1
5 Fanfare Band 1
6 Clarinet 2
7 Saxophone 2
8 Trumpet 2
On the other hand I have a table that stores the musicID that is linked to a categoryID
id categoryID musicID
1 4 1
2 8 1
3 3 2
4 6 2
I need the following result from a query:
musicID instrumentation solo_instrument
1 Brass Band Trumpet
2 Concert Band Clarinet
I have been told to use a tree structure as in the future it is likely that other categories are added and this should be able to support that. However, I am not able to figure out how to write a query to get the result above.
I kind of get the result I want when selecting first the instrumentation, second the solo_instrument, but this is all hardcoded and does not allow for music tracks to only have one parentID as I select them individually.
Is this even possible or should I overhaul my database structure? I'd like to see your recommendations.
You should be able to tackle this using conditional aggregation.
Query :
SELECT
mu.musicID,
MAX(CASE WHEN cp.name = 'instrumentation' THEN ca.name END) instrumentation,
MAX(CASE WHEN cp.name = 'solo_instrument' THEN ca.name END) solo_instrument
FROM
musics mu
INNER JOIN categories ca ON ca.id = mu.categoryID
INNER JOIN categories cp ON cp.id = ca.parentID
GROUP by mu.musicID
The INNER JOINs pull up the corresponding category, and then goes up one level to find the parent category. If new root categories are created, you would just need to add more MAX() columns to the query.
In this DB Fiddle demo with your sample data, the query returns :
| musicID | instrumentation | solo_instrument |
| ------- | --------------- | --------------- |
| 1 | Brass Band | Trumpet |
| 2 | Concert Band | Clarinet |
First you group by musicid in table_music and the join twice to table_categories:
select t.musicid, c1.name instrumentation, c2.name solo_instrument
from (
select musicid, min(categoryid) instrumentationid, max(categoryid) solo_instrumentid
from table_music
group by musicid
) t inner join table_categories c1
on c1.id = t.instrumentationid
inner join table_categories c2
on c2.id = t.solo_instrumentid
order by t.musicid

How to list all row of a table and when join with another table is possible only the first row of it?

I'm setting up an "order management" application which should provide a datalist listing up all my catalog item and show for each of them the "last order date". I also want the items not ordered yet to be reflecting without last order date.
I have tried this query without success
SELECT i.id, i.name, d.name
FROM items i
JOIN LEFT orders o
ON i.id = o.items
I have these tables in my MySQL database:
ITEMS
ID|Name
-----------
0 |Table
1 |Chair
2 |Screen
3 |Speaker
4 |Keyboard
5 |Laptop
6 |Mug
ORDERS
ID|ITEMS|DATE
-----------------
0 | 0 |20190122
1 | 0 |20181231
2 | 1 |20180601
3 | 3 |20180122
4 | 2 |20171231
5 | 4 |20180501
4 | 6 |20171115
5 | 3 |20180101
And would like the following output :
LAST_ORDER_DATE
ID|NAME |DATE
--------------------
0 |Table |20190122
1 |Chair |20180601
2 |Screen |20171231
3 |Speaker |20180122
4 |Keyboard|20180501
5 |Laptop |
6 |Mug |20171115
Thanks for any help
seems you need max date so you could use max( ) function and group by
select i.id, i.name, max(o.date)
FROM items i
LEFT JOIN orders o ON i.id = o.items
group by i.id, i.name
you can also convert the existing string (if it is a char field) to date format like the following query if needed
select i.id, i.name, max(str_to_date(date,'%Y%m%d'))
FROM items i
LEFT JOIN orders o ON i.id = o.items
group by i.id, i.name
Try this :
change the table and column names as you have:
SELECT
t1.name,
MAX(t2.pdate) AS last_order_date
FROM
test t1
LEFT JOIN test1 t2 ON t1.id = t2.items
GROUP BY
t2.items,
t1.name
ORDER BY
last_order_date;
Tables :
Output :
if you are using string value in date then convert to date.

How to retrieve datasets from normalised shema in MySQL 5.7?

I am trying to retrieve datasets from a normalised MySQL 5.7 table shema where I am struggling to get the values.
There are 4 tables: https://www.db-fiddle.com/f/q2PJZVegdWXnpkotN2utu2/0
Table1: articles
article_id | title
1 First Car
2 Second Car
Table2: articles_attr
article_id | attr_id
1 1
1 2
1 3
1 5
2 3
2 4
Table3: attr_groups
attr_id | attr_group_id | attribute
1 1 red
2 2 diesel
3 3 automatic
4 3 airbag
5 3 radio
Table4: attr_groups_names
attr_group_id | name
1 color
2 engine
3 features
Now I would like to retrieve all datasets (car1, car2, ..) with all attributes where the ones with multiple attributes per group get agregated.
e.g.
article_id | title | color | engine | features
1 Car 1 red diesel automatic,radio
2 ...
The amount of groups is huge (20+), so I would like to avoid to many joins.
My best shot:
SELECT
a.article_id,
a.title,
GROUP_CONCAT(CASE attr.attr_group_id WHEN 26 THEN cat.attr_de END) AS functions,
GROUP_CONCAT(CASE attr.attr_group_id WHEN 27 THEN cat.attr_de END) AS miscellaneous
FROM articles_attr AS attr
INNER JOIN articles a ON a.article_id = attr.article_id
INNER JOIN articles_attr AS cat ON cat.attr_id = attr.attr_id
GROUP BY a.article_id
LIMIT 3
How can this be done?
Your query has a correct basic structure, but your CASE expressions look somewhat off. Try this version:
SELECT
a.article_id,
a.title,
GROUP_CONCAT(CASE WHEN agn.name = 'color' THEN ag.attribute END) color,
GROUP_CONCAT(CASE WHEN agn.name = 'engine' THEN ag.attribute END) engine,
GROUP_CONCAT(CASE WHEN agn.name = 'features' THEN ag.attribute END) features
FROM articles a
INNER JOIN articles_attr aa
ON a.article_id = aa.article_id
INNER JOIN attr_groups ag
ON ag.attr_id = aa.attr_id
INNER JOIN attr_groups_names agn
ON agn.attr_group_id = ag.attr_group_id
GROUP BY
a.article_id,
a.title;
Demo
GROUP_CONCAT works here by ignoring NULL values in the aggregation, which then do not get added to the concatenated string. Note also that depending on your MySQL version, you might have to GROUP BY both the id and title in the articles table.

JOIN on array of ids in sql

I have three tables:
TABLE 1 contracts
-------------------
id | contract_name
--------------------
1 test name
2 test name 2
2 test name 3
4 test name 4
TABLE 2 rooms
-------------------
id | room_name
--------------------
1 test name
2 test name 2
2 test name 3
4 test name 4
TABLE 3 promos
----------------------------------
id | contracts_id | rooms_id
----------------------------------
1 1,3 1,3,4
1 2 1,2,3
No I am trying to do an inner join to get the names of the contract and the rooms according to the ids in the array saved in database. I know this is not ideal at all, but I can not change the database set up. So here is what I would like to do with my query, but obviously it is impossible. Does anyone have any idea on how I can accomplish this?
mysql_query("SELECT pb.*, c.contract_name, r.room_name FROM promo_blackouts AS pb
INNER JOIN contracts as c ON c.contract_id IS IN pb.contracts_id
INNER JOIN rooms as r ON r.room_id IS IN pb.rooms_id
WHERE pb.promo_id = '$promo_id'") or die(mysql_error());
Are you looking for something like this?:
SELECT DISTINCT
contract_name,
room_name
FROM
promos
INNER JOIN
contracts ON FIND_IN_SET(contracts.id, contract_id) != 0
INNER JOIN
rooms ON FIND_IN_SET(rooms.id, room_id) != 0
WHERE
promos.id = 1

SQL use of conditional AND on a join table to ensure at least two or multiple values are matched

I have a join table named languages_services that basically joins the table services and languages.
I need to find a service that is able to serve both ENGLISH (language_id=1) and ESPANOL (language_id=2).
table languages_services
------------------------
service_id | language_id
------------------------
1 | 1
1 | 2
1 | 3
2 | 1
2 | 3
With the data provided above, I want to test for language_id=1 AND language_id=2 where the result would look like this
QUERY RESULT
------------
service_id
------------
1
Obviously it doesn't return the one with service_id=2 because it doesn't service Espanol.
Any tips on this is greatly appreciated!
SELECT
service_id
FROM
language_services
WHERE
language_id = 1
OR language_id = 2
GROUP BY
service_id
HAVING
COUNT(*) = 2
Or...
WHERE
lanaguage_id IN (1,2)
GROUP BY
service_id
HAVING
COUNT(*) = 2
If you're always looking at 2 languages you could do it with joins, but the aggregate version is easier to adapt to differing numbers of language_ids. (Add an OR, or add an item to the IN list, and change the COUNT(*) = 2 to COUNT(*) = 3, etc, etc).
Be aware, however, that this scales very poorly. And with this table structure there isn't much you can do about that.
EDIT Example using a join for 2 languages
SELECT
lang1.service_id
FROM
language_services AS lang1
INNER JOIN
language_services AS lang2
ON lang1.service_id = lang2.service_id
WHERE
lang1.language_id = 1
AND lang2.language_id = 2