I need some help with dynamical column names in a sql query. First I will try to explain my database structure and then the problem.
Database structure:
admin_group table:
+--------+----------------+
| id | language_code |
+--------+----------------+
| 1 | en_UK |
| 2 | de_DE |
| 3 | es_ES |
+--------+----------------+
constructions_meta table:
+--------+-----------------+----------+
| id | admin_group_FK | value |
+--------+-----------------+----------+
| 1 | 1 | 0.13 |
| 2 | 2 | 0.12 |
| 3 | 3 | 0.10 |
+--------+-----------------+----------+
construction_lang table:
+--------+-----------------+----------------+----------------+
| id | en_UK_name | de_DE_name |es_ES_name |
+--------+-----------------+----------------+----------------+
| 1 | Construction 1 | Konstruktion 1 | Construcción 1 |
| 2 | Construction 2 | Konstruktion 2 | Construcción 2 |
| 3 | Construction 3 | Konstruktion 3 | Construcción 3 |
+--------+-----------------+----------------+----------------+
Those are my tables in the database. What I need here is to get the names of the constructions regarding the language code for each construction. For example I want to list the constructions as following:
Construction 1
Konstruktion 2
Construcción 3
select c.id , (select case c.admin_group_FK
when 1 then l.en_UK_name
when 2 then l.de_DE_name
else l.es_ES_name
end from construction_lang l where id = c.id) as construction_name,
c.value
from constructions_meta c;
Yes the solution is only a get-you-working-quickly one .
And Rob is correct about improving the data model
Solution is :
you need to and drop the construction_lang table from your data model and replace it with this association table much like what Rob proposes
+------------------+--------------+----------------+
| construction_fk | language_fk | name |
+------------------+--------------+----------------+
| 1 | 1 | Construction 1 |
| 1 | 2 | Konstruktion 1 |
| 1 | 3 | Construcción 1 |
| 2 | 1 | Construction 2 |
| 2 | 2 | Konstruktion 2 |
| 2 | 3 | Construcción 2 |
| 3 | 1 | Construction 3 |
| 3 | 2 | Konstruktion 3 |
| 3 | 3 | Construcción 3 |
+------------------+--------------+----------------+
then query you require is
select c.id, l.language_code , a.name ,c.value
from constructions_meta c
join construction_lang_assoc a on a.construction_fk = c.id and a.language_fk = c.admin_group_FK
join admin_group l on l.id = c.admin_group_FK;
It's probably easiest to do that in two passes. That'll give you the ability to expand the width of the construction_lang table without affecting the SQL statements significantly.
Firstly you issue a SQL statement to get the column names that you need, then you use that result set to build a second SQL statement that will get the names for you.
This is not the ideal solution, as it is working around the data-model.
Given a query that returns back the id and language from admin_group in line with:
array( array( 'id' => 1, 'language_code' => 'en_UK' ),
array( 'id' => 2, 'language_code' => 'de_DE' ),
array( 'id' => 3, 'language_code' => 'es_ES' ) )
You can build a statement with something along the lines of (using the other answer from diarmuid as an example)
$sql = "select c.id , (select case c.admin_group_FK\r\n";
foreach( $languages as $thisLanguage ) {
$sql .= "when {$thisLanguage['id']} then l.${thisLanguage['language_code']}_name\r\n";
}
$sql .= ...
This is a round-about way of doing it, because of the data-model you have. Ideally you wouldn't need the "dynamic" SQL."
If you want to remove the round-about-ness, and build something more in line with standard relational database theory, then you can change the model so that the construction_lang table is more like this:
+------------------------+-----------------+----------------+
| constructions_meta_fk | language | name |
+------------------------+-----------------+----------------+
| 1 | en_UK | Construction 1 |
| 1 | de_DE | Konstruktion 1 |
| 1 | es_ES | Construcción 1 |
| 2 | en_UK | Construction 2 |
| 2 | de_DE | Konstruktion 2 |
| 2 | es_ES | Construcción 2 |
| 3 | en_UK | Construction 3 |
| 3 | de_DE | Konstruktion 3 |
| 3 | es_ES | Construcción 3 |
+------------------------+-----------------+----------------+
you can use if condtion like `
if(admin_group_FK == 1)
{
fetch['en_UK_name'];
}
if(admin_group_FK == 2)
{
fetch['de_DE_name'];
}
if(admin_group_FK == 3)
{
fetch['es_ES_name'];
}
`
Related
This question already has answers here:
Can I concatenate multiple MySQL rows into one field?
(16 answers)
MySQL DISTINCT on a GROUP_CONCAT()
(6 answers)
Closed 4 years ago.
I want to create an advanced search based on one table and other tables
my tables:
estate :
+------+-------------+-------------------+-------------------+
| id | title | cat_id | date |
+------+-------------+-------------------+-------------------+
| 1 | test1 | 1 | 1526793203 |
| 2 | test2 | 2 | 1526793203 |
| 3 | test3 | 3 | 1526793203 |
+------+--------------+------------------+-------------------+
estate_risk
+------+-------------+----------------+--------------+
| id | estate_id | title | consequence |
+------+-------------+----------------+--------------+
| 3 | 1 | risktitle1 | 123 |
| 4 | 1 | risktitle2 | 433 |
| 5 | 1 | risktitle3 | 523 |
| 6 | 2 | risktitle4 | 976 |
| 7 | 2 | risktitle5 | 422 |
| 8 | 3 | risktitle6 | 124 |
+------+-------------+----------------+--------------+
related_estate
+------+-------------+----------------+--------------+
| id | estate_id | title | storage |
+------+-------------+----------------+--------------+
| 3 | 1 | testdata | 1 |
| 4 | 1 | testdata2 | 2 |
| 5 | 1 | testdata3 | 3 |
| 6 | 2 | testdata4 | 4 |
| 7 | 2 | testdata5 | 5 |
| 8 | 5 | testdata6 | 6 |
+------+-------------+----------------+---------------+
And some other tables...(foreign all tables is estate_id)
I want to get all the data in a row,in other words for example, I have several related data in the estate_risk table and when I use join query get this result data :
sample query one join:
SELECT R.id,R.title,C.title,C.storage FROM estate R LEFT JOIN estate_risk as C ON estate_risk .estate_id = R.id
result :
----------+------------------+----------------+--------------+
R.id | R.title | C.title | C.consequence
----------+------------------+----------------+--------------+
1 | test1 | risktitle1 | 123 |
1 | test1 | risktitle2 | 433 |
1 | test1 | risktitle3 | 523 |
2 | test2 | risktitle4 | 976 |
2 | test2 | risktitle5 | 422 |
3 | test3 | risktitle6 | 124 |
----------+------------------+----------------+--------------+
Everything is right but i want get all the data in a row that's mean only a row R.id with all C.title in one row
My main goal is to display the page search results just one estate with all other data tables
+------+-------------+----------------+----------------+
id | estate_title| estate_risk | related_estate |
+------+-------------+----------------+----------------+
1 test1 risktitle1 testdata1
risktitle2 testdata2
risktitle3 testdata3
---------------------------------------------------------
2 ...
Maybe my goal is not right, but I do not know how I can get this output And this should be done on the database side, or programming side ?
What ideas can be made on the programming side?
It looks like what you want is a GROUP_CONCAT to put together the risktitles and related_estates.
GROUP_CONCAT(C.title SEPARATOR '\n'),
GROUP_CONCAT(Some_other_column SEPARATOR '\n')
Once you have your group functions, I believe grouping by R.id as Yanet suggested and the data should come together correctly.
If you do have larger data sets though, there are limitations to MySQL's concatenation functions. So in that case you might need to return the data as you are already and then reformat it to match what you want in the PHP side.
Group Concat Limit
I'm developing a PHP script, and I have the following table:
+----+-----------+----------+--------------+
| id | id_parent | position | feature |
+----+------------+---------+--------------+
| 1 | 1 | 2 | -B-A-C- |
| 2 | 1 | 3 | -B-C- |
| 3 | 2 | 4 | -C-B- |
| 4 | 3 | 1 | -A-B- |
| 5 | 1 | 6 | -A-C- |
| 6 | 2 | 5 | -C-B- |
| 7 | 2 | 7 | -B-C- |
| 8 | 3 | 8 | -A- |
+----+-----------+----------+--------------+
From this table I would like to select all the rows with "feature" LIKE "%-A-%", but displaying first the result with lowest "position", then all the rows that have same value for column "id_parent" of the first result, then row with the 2nd lowest "position" and all the rows that have same "id_parent" of the result with the 2nd lowest "position", and so on...
So the final result should be:
+----+-----------+----------+--------------+
| id | id_parent | position | feature |
+----+------------+---------+--------------+
| 4 | 3 | 1 | -A-B- |
| 8 | 3 | 8 | -A- |
| 1 | 1 | 2 | -B-A-C- |
| 5 | 1 | 6 | -A-C- |
+----+-----------+----------+--------------+
For some reason I can't explain here I need to have and HAVING clause for selecting the right "feature" value (...HAVING 'feature' LIKE '%-A-%' ...).
Is it possible to make all this with MySQL (possibly without subqueries) or by processing data results with PHP?
Does this help? I've left the last part of the problem as an exercise for the reader...
SELECT a.*
, c.*
FROM my_table a
JOIN
( SELECT id_parent, MIN(position) position FROM my_table WHERE feature = 'a' GROUP BY id_parent ) b
ON b.id_parent = a.id_parent
AND b.position = a.position
JOIN my_table c
ON c.feature = a.feature
AND c.id_parent = a.id_parent;
I have a table with concatenated values within both rows, I am therefore uniquely retrieve ranking for each row in the tables.
UPDATE
The other tables has been added to question
NamesTable
NID | Name |
1 | Mu |
2 | Ni |
3 | ices |
GroupTable
GID | GName |
1 | GroupA |
2 | GroupB |
3 | GroupC |
MainTable
| NID | Ages | Group |
| 1 | 84 | 1 |
| 2 | 64 | 1 |
| 3 | 78 | 1 |
| 1 | 63 | 2 |
| 2 | 25 | 2 |
| 3 | 87 | 2 |
| 1 | 43 | 3 |
| 2 | 62 | 3 |
| 3 | 37 | 3 |
Now the first Name is equated to the first age in the table, I am able to equate them using php and foreach statements, Now the problem is with the ranking of the ages per each group. I am ranking the names uniquely on each row or group.
Results which is expected
| Names | Ages | Group | Ranking |
| Mu,Ni,ices | 84,64,78 | 1 | 1,3,2 |
| Mu,Ni,ices | 63,25,87 | 2 | 2,3,1 |
| Mu,Ni,ices | 43,62,37 | 3 | 2,1,3 |
In my quest to solving this, I am using GROUP_CONCAT, and I have been able to come to this level in the below query
SELECT
GROUP_CONCAT(Names) NAMES,
GROUP_CONCAT(Ages) AGES,
GROUP_CONCAT(Group) GROUPS,
GROUP_CONCAT( FIND_IN_SET(Ages, (
SELECT
GROUP_CONCAT( Age ORDER BY Age DESC)
FROM (
SELECT
GROUP_CONCAT(Ages ORDER BY Ages DESC ) Age
FROM
`MainTable` s
GROUP by `Group`
) s
)
)) rank
FROM
`MainTable` c
GROUP by `Group`
This actually gives me the below results.
| Names | Ages | Group | Ranking |
| 1,2,3 | 84,64,78 | 1 | 7,9,8 |
| 1,2,3 | 63,25,87 | 2 | 5,6,4 |
| 1,2,3 | 43,62,37 | 3 | 2,1,3 |
The only problem is that the ranking Values increase from 1 to 9 instead of ranking each row uniquely. Its there any idea that can help me cross over and fix this? I would be grateful for your help. Thanks
I have two tables, which looks like below. In the employee table instead mentioning the skills directly, I'm using as reference from another table.
Question:
select * from Employee where Skills = "1";
The above query will show the employee record who has ONLY the "Python" skill. Certainly, it won't show the other combination in which Python is included, such as "1,2" (Python, Java). How can i achieve this without using like operator, since in case if i have 10, 11, 21 in skills it'll be problem isn't it.
If you feel this way of using reference is difficult or not recommend, please suggest your idea :-)
Employee table:
+-----+-------------+-------------+
| id | Name | Skills |
+-----+-------------+-------------+
| 1 | Xyz | 1,2,4 |
| 2 | Xyy | 1,3 |
| 3 | Abc | 1,2,3 |
| 4 | Asd | 1 |
+-----+-------------+-------------+
Skillset table:
+-----+-------------+
| id | SkillSet |
+-----+-------------+
| 1 | Python |
| 2 | Java |
| 3 | C |
| 4 | PHP |
+-----+-------------+
Consider the following
mysql> select * from employee ;
+------+------+--------+
| id | name | skills |
+------+------+--------+
| 1 | xyz | 1,2,4 |
| 2 | abc | 1,3 |
| 3 | lmn | 1,2,3 |
+------+------+--------+
3 rows in set (0.00 sec)
mysql> select * from skillset ;
+------+----------+
| id | skillset |
+------+----------+
| 1 | Python |
| 2 | Java |
| 3 | C |
| 4 | PHP |
+------+----------+
4 rows in set (0.00 sec)
This structure is similar to yours and to do query in these situation we can however use find_in_set but thats pretty inefficient , here are few examples
mysql> select e.id,
e.name,
group_concat(s.skillset) as skillset
from employee e join skillset s
on find_in_set(s.id,e.skills) > 0
where find_in_set(1,e.skills) > 0
group by e.id ;
+------+------+-----------------+
| id | name | skillset |
+------+------+-----------------+
| 1 | xyz | Python,Java,PHP |
| 2 | abc | C,Python |
| 3 | lmn | Java,Python,C |
+------+------+-----------------+
3 rows in set (0.00 sec)
select e.id,
e.name,
group_concat(s.skillset) as skillset
from employee e
join skillset s on find_in_set(s.id,e.skills) > 0
where find_in_set(2,e.skills) > 0 group by e.id ;
+------+------+-----------------+
| id | name | skillset |
+------+------+-----------------+
| 1 | xyz | Python,PHP,Java |
| 3 | lmn | C,Java,Python |
+------+------+-----------------+
Now a proper normalization would make life much simpler and will have the following association table
mysql> select * from employee_skills;
+------------+----------+
| idemployee | idskills |
+------------+----------+
| 1 | 1 |
| 1 | 2 |
| 1 | 3 |
| 2 | 1 |
| 2 | 3 |
| 3 | 1 |
| 3 | 2 |
| 3 | 3 |
+------------+----------+
Now doing query will be much more efficient in this case
mysql> select e.id,
e.name,
s.skillset from employee e
join employee_skills es on es.idemployee = e.id
join skillset s on s.id = es.idskills where s.id = 1 ;
+------+------+----------+
| id | name | skillset |
+------+------+----------+
| 1 | xyz | Python |
| 2 | abc | Python |
| 3 | lmn | Python |
+------+------+----------+
Using the last approach more complex calculations could be done pretty easily.
As mentioned by others in the comments, this is not the most useful design choice as it would make it long winded to create a simple CRUD interface to interact with these values.
You would have ideally:
employee table:
+-----+-------------+
| id | name |
+-----+-------------+
| 1 | Bob |
| 2 | Mary |
+-----+-------------++
skillset table:
+-----+-------------+
| id | skillset |
+-----+-------------+
| 1 | Python |
| 2 | Java |
| 3 | C |
| 4 | PHP |
+-----+-------------+
employee_skillset table:
+-----+---------------+---------------+
| id | employee_id | skillset_id |
+-----+---------------+---------------+
| 1 | 1 | 1 |
| 2 | 1 | 2 |
| 3 | 1 | 4 |
| 4 | 2 | 1 |
| 5 | 2 | 3 |
+-----+---------------+---------------+
Then you could do:
SELECT *
FROM employee e
INNER JOIN employee_skillset es
ON e.id = es.employee_id
WHERE es.skillset_id = "1";
You could use the skillset table in the CRUD interface as selectable / editable options.
EDIT:
It's easy enough to include a range of skills within this too using the IN clause:
WHERE es.skillset_id IN (1, 3, 4);
Would like to get the following as a result from the table structure below (MYSQL + PHP)
array[0][name]1,[desc]red,[title]hero,[desc]strong,[desc2]smells,[img][0]red1,[img][1]red2,[img][2]red3,ext[0].jpg,[ext][1].gif,[ext][2].png,[count][0]253,[count][1]211,[count][2]21,[count][3]121,[dist][0]5,[dist][1]5,[dist][2]12,[dist][3]2,[score][0]2,[score][1]3,[score][2]1,[score][3]5,[score][4]4,[val][0]5,[val][1]1,[val][2]4,[val][3]3,[val][4]4
The problem I have with a simple SELECT, JOIN and GROUP_CONCAT is that the values duplicate after selecting all the images.
I've tried various other ways for example selecting the data by row combined with a foreach loop in PHP, but I end up with lots of duplicates, and it looks very messy.
I also though about splitting it into multiple selects instead of using one, but I really would like to know if it can be done with one select.
Could someone help me with an MYSQL select? Thanks
game
+-----+----------+
| pid | name |
+-----+----------+
| 1 | red |
| 2 | green |
| 3 | blue |
+-----+----------+
detail
+-----+------+--------+-------+--------+
| id | pid | title | desc | desc 2 |
+-----+------+--------+-------+--------+
| 1 | 1 | hero |strong | smells |
| 2 | 2 | prince |nice | tall |
| 3 | 3 | dragon |big | green |
+-----+------+--------+-------+--------+
image
+-----+-----+-----+----+
| id | pid | img |ext |
+-----+-----+-----+----+
| 1 | 1 | red1|.jpg|
| 2 | 1 | red2|.gif|
| 3 | 1 | red3|.png|
+-----+-----+-----+----+
devmap
+-----+-----+-------+------+
| id | pid | count | dist |
+-----+-----+-------+------+
| 1 | 1 | 253 | 5 |
| 2 | 1 | 211 | 5 |
| 3 | 1 | 21 | 12 |
| 4 | 1 | 121 | 2 |
+-----+-----+-------+------+
stats
+-----+-----+-------+------+
| id | pid | scrore| val |
+-----+-----+-------+------+
| 1 | 1 | 2 | 5 |
| 2 | 1 | 3 | 1 |
| 3 | 1 | 1 | 4 |
| 4 | 1 | 5 | 3 |
| 5 | 1 | 4 | 3 |
+-----+-----+-------+------+
When you do a JOIN that involves more than a 1:1 mapping between tables you're going to have duplicate data, and there's no way to get around that in the query.
You can break it out into multiple selects, or you can loop through the result set and pare out whatever duplicate information you don't want.