MySql - using dynamic table names in one query - mysql

I have the following tables:
cars
id
name
color
bicycles
id
name
number_of_gearshift
I need a central index table of this tables in my mysql database and a unique id for them. Something like this:
items
id
table_name
Lets say, the id in the items-table is the same as in the corresponding table:
items
id | table_name
1 | cars
2 | cars
3 | bicycles
4 | cars
cars
id | name | color
1 | Peugeot | red
2 | BMW | green
4 | Nissan | blue
bicycles
id | name | number_of_gearshift
3 | Stevens | 24
My question - the following situation:
I have the ID (for example XXX) of an item. Now I want to get the data of this item, by only one query. Something like this (I know, that will not work):
SELECT table2.*
FROM (SELECT table_name FROM items WHERE id = XXX) AS table2
Is it possible?

Use can use a dynamic sql query to achieve this.
set #query = null;
set #id = 3;/*change according to requirement*/
SET #tn := (select `table_name` from items where id = #id);
set #query = concat('select * from ',#tn,' where id = ',#id);
prepare stmt from #query;
execute stmt;
deallocate prepare stmt;
Change the value of #id according to your requirement.
SQL Fiddle

Related

Retrieving data from another column based on entries in another column within same table

I have this table.
+----+-----------+--------+----------+
| sku| strength | color | option_1 |
+----+-----------+--------+----------+
| 1 | 40 lb | (NULL) | strength |
| 2 | 50 lb | black | color |
| 3 | (NULL) | black | color |
| 4 | (NULL) | red | color |
+----+-----------+--------+----------+
I want this table:
+----+-----------+--------+----------+----------------+
|sku | strength | color | option_1 | option_value_1 |
+----+-----------+--------+----------+----------------+
| 1 | 40 lb | (NULL) | strength | 40 lb |
| 2 | 50 lb | black | color | black |
| 3 | (NULL) | black | color | black |
| 4 | (NULL) | red | color | red |
+----+-----------+--------+----------+----------------+
I'm new to mysql. I'm trying to do this index-match or vlookup like query where option_1 determines which column to retrieve data from and send to option_value_1. I wasn't sure if I should do an inner join or a case clause since I have a much larger table with a lot more options than just color and strength.
You can use a simple searched case expression, such as
case option_1
when 'strength' then strength
when 'color' then color
when '...' then ...
else 'default option'
end as option_value_1
...
I don't think you need a JOIN here. However, since you said there a lot more columns then it's going to be a long CASE expression to write. Also, if there's new column added then you need to remember to update the query from time to time. One way you can prevent that is using Prepare statement :
Setting variables as NULL just in case
SET #sql := NULL;
SET #columns := NULL;
Constructing the CASE expression and assign to #columns variable:
SELECT GROUP_CONCAT(DISTINCT
CONCAT('WHEN "',option_1,'" THEN ',option_1) SEPARATOR ' ')
INTO #columns
FROM mytable;
Constructing the final query by appending the previously constructed #columns variable into the #sql variable:
SELECT CONCAT('SELECT *, CASE option_1 ',#columns,' END AS option_value_1 FROM mytable;')
INTO #sql;
Prepare, execute and deallocate the final generated query in #sql:
PREPARE stmt FROM #sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
To check the variables you can run:
SELECT #columns;
SELECT #sql;
This will be good for you to test the query especially when the execution returned error; so that you can pin-point where the problem is and fix.
Note:
It's not necessary to have two variables in constructing the final query. I mean, you can just do a straight query construction to #sql by combining the #columns generating query with the #sql generating query. I personally separate them to make it easier to understand the components.
Demo fiddle

Concat is not working in mysql inclause

Please help me, i try to fetch category list as like below, where category ids is passed in inclause, it returns two rows.
mysql> select c_id,c_name from category where c_id in (870,854);
+------+---------------+
| c_id | c_name |
+------+---------------+
| 854 | Telugu |
| 870 | Telugu Events |
+------+---------------+
Whereas same category id is concatenated and passed to inclause as parameter, but its returning only one row insted of two rows.
mysql> select c_id,c_name from category where c_id in (select concat(870,',',854) as c_id);
+------+---------------+
| c_id | c_name |
+------+---------------+
| 870 | Telugu Events |
+------+---------------+
Please clarify me.
Thanks.
Not sure why you want to use concat in the in-clause, this is needed when you have some dynamic data, and for this you need to use dynamic query using prepare
Here how it is done
set #c_id := concat(870,',',854);
set #qry = concat("select * from category where c_id in (",#c_id,")");
prepare stmt from #qry;
execute stmt;
DEMO
I found the solution,
SELECT c_id, c_name
FROM category
WHERE FIND_IN_SET(c_id, (SELECT CONCAT(870,',',854) AS c_id))
Thanks.

MYSQL variable tablename from value

I'm trying to get my head around the following sql problem:
I have an ACTIONS table that contains the following:
------------------------------------
| ACTIONS |
|----------------------------------|
| ID |
| GROUP_ID |
| TABLENAME |
| FEATURE_ID |
------------------------------------
And a bunch of tables that look like this:
------------------------------------
| GRASS or SAND or ... |
|----------------------------------|
| FEATURE_ID |
| POSITION |
|+(more columns depending on table)|
------------------------------------
Now the ACTIONS.TABLENAME points to a certain table (for example: GRASS or SAND or ...)
All these tables have a column called position
I would now like to query all actions from the ACTIONS table with their respective POSITION values.
How can i tell the query to go and look for the position values in their correct tables?
Thank you for pointing me in the right direction!
Max
You cannot do this directly as Mysql does not support joining to a variable table name.
The 2 solutions are to either generate the SQL dynamically (either in your scripting language or in an sql procedure) if you know that you will be joining to a particular link table, or building up a unioned query with one sub query per table.
SELECT actions.id, actions.group_id, actions.tablename, actions.feature_id, grass.position
FROM actions
INNER JOIN grass
ON actions.feature_id = grass.feature_id
WHERE actions.tablename = 'grass'
UNION
SELECT actions.id, actions.group_id, actions.tablename, actions.feature_id, sand.position
FROM actions
INNER JOIN sand
ON actions.feature_id = sand.feature_id
WHERE actions.tablename = 'sand'
UNION
........
You can use CONCAT & PREPARE for achieving what you asked for
Check this: MySQL Prepare
(search for 'how to choose the table on which to perform a query at runtime, by storing the name of the table as a user variable')
mysql> USE test;
mysql> CREATE TABLE t1 (a INT NOT NULL);
mysql> INSERT INTO t1 VALUES (4), (8), (11), (32), (80);
mysql> SET #table = 't1';
mysql> SET #s = CONCAT('SELECT * FROM ', #table);
mysql> PREPARE stmt3 FROM #s;
mysql> EXECUTE stmt3;
+----+
| a |
+----+
| 4 |
| 8 |
| 11 |
| 32 |
| 80 |
+----+
mysql> DEALLOCATE PREPARE stmt3;

Get column name dynamically by Specific row value

I am struck at writing a query.
Here I want to show the column name based on some specific value
For Instance, my table is like this:
id | fruits |vegetables |softdrink
-----------------------
1 | apple | Onion | Pepsi
2 | mango | Potato | Coke
3 | banana | Bringal | RedBull
If I have a value "mango", then I should get the column name as fruit or
If I have a value "RedBull", then I should get the column name as softdrink
NOTE: I have many columns around 48 to get the name from any one of them
set #q= CONCAT('SELECT columns.column_name
from table inner
join information_schema.columns
on columns.table_schema = "dbname"
and columns.table_name = "table"
and ((',
(SELECT GROUP_CONCAT(CONCAT('columns.column_name="',column_name,'"',' and table.',column_name,' = "value','"') SEPARATOR ' OR ')
FROM INFORMATION_SCHEMA.COLUMNS
WHERE table_name = 'table'),
'))');
prepare query from #q;
execute query;
This works for sure..
Phew!
Fiddle: http://sqlfiddle.com/#!2/9420c/2/2
PS: Replace table with your table name ,dbname with your db name and value with your value
As you encrypted your question as much as possible, I can only guess.
this table smells of a bad design. And it should be like
col | value
col1 | a
col1 | b
col2 | d
and so on.
than you can easily get your col out from value

join tables and transpose columns and rows

I have one table that looks like this called survey_1:
================================================
|id | token | 1X2X1 | 1X2X2 |
=====+========+===============+================|
| 1 | 1 | YES | Justin Beiber |
|----+--------+---------------+----------------|
| 2 | 1 | YES | Britney Spears |
|----+--------+---------------+----------------|
note: 1X2X1 represents- survey-id X group-id X question-id
I have another table called survey_questions:
===============================================================
|sid | gid | qid | question |
=====+========+===============+===============================|
| 1 | 2 | 1 | Do you listen to music? |
|----+--------+-----------------------------------------------|
| 1 | 2 | 2 | Who is your favorite music artists? |
|----+--------+-----------------------------------------------|
The sid (survey-id), gid (group-id) and qid(question-id) define that specific question in this table
I need a query that will give me a result like this:
======================================================
| Question | Answer |
=========+===========+===============================|
| Do you listen to music? | YES |
|----------------------------------------------------|
| Who is your favorite music artists? | Justin Beiber|
|----------------------------------------------------|
NOTE: My database contains thousands of these columns, so it would be very time consuming to edit every survey to match up perfectly in this format.
Can anyone help out with this? Thank you
Can you change the table schema? Cause that first table, survey_1 is better written with one row per answer and with the entire key of the other table per row. Like this (add your own indexes)
create table survey_1 (
id int,
token int,
sid int,
gid int,
qid int,
answer varchar(255)
)
Than the data would be
------------------------------------------
| 1 | 1 | 1 | 2 | 1 | "YES" |
| 1 | 1 | 1 | 2 | 2 | "Justin Beiber" |
| 2 | 1 | 1 | 2 | 1 | "YES" |
| 2 | 1 | 1 | 2 | 2 | "Britney Spears" |
------------------------------------------
It's going to be much easier to work with and generally a better design.
Here is how it would look http://sqlfiddle.com/#!2/4f1ca/2
Create a view for each survey. For the old surveys a simple script should be able to do it, for new surveys make it a part of the process when creating new surveys. This is how the view could look for survey_1
create or replace view v_survey_1 as
select id, question, 1X2X1 as answer
from question
join survey_1 s
where sid = 1
and gid = 2
and qid = 1
union
select id, question, 1X2X2
from question
join survey_1 s
where sid = 1
and gid = 2
and qid = 2
;
http://sqlfiddle.com/#!2/63aee/1
To build the views a script would roughly do like this.
Find all tables to build views on by running
select table_name
from information_schema.tables
where table_schema = 'test'
and table_name like 'survey\_%';
For each view find the union parts by running this for its table
select column_name
from information_schema.columns
where table_name = 'survey_1'
and column_name regexp '^[0-9]+X[0-9]+X[0-9]+$';
Extract the number parts and use them when comparing with sid, gid and qid.
This script could also be used to populate new proper tables.
You need to use 'UNPIVOT', which MySQL unfortunately does not support. You can do a similar thing by hardcoding the column names (but you need to know all the columns in advance) like this:
SELECT survey_questions.Question,
CASE survey_questions.qid
WHEN 1 THEN survey_1.`1X2X1`
WHEN 2 THEN survey_1.`1X2X2`
WHEN 3 THEN survey_1.`1X2X3`
WHEN 4 THEN survey_1.`1X2X4`
// ...
END as Answer
FROM survey_questions
JOIN survey_1
ON survey_questions.qid = survey_1.id
AND survey_questions.gid = survey_1.token_id
WHERE survey_questions.sid = 1
Of course, you can always use some scripting language to generate the column names for you... For example, here is a stored procedure you can make:
CREATE PROCEDURE 'get_qa_for_survey'
(
IN surveyId INT
)
BEGIN
DECLARE query1 TEXT;
SET #tableName = 'survey_' + surveyId;
SET query1 = 'SELECT survey_questions.Question,
CASE survey_questions.qid ';
DECLARE col_names CURSOR FOR
SELECT column_name
FROM INFORMATION_SCHEMA.COLUMNS
WHERE table_name = #tableName
AND (column_name LIKE surveyId +'X%');
ORDER BY ordinal_position;
select FOUND_ROWS() into num_rows;
SET i = 1;
the_loop: LOOP
IF i > num_rows THEN
CLOSE col_names;
LEAVE the_loop;
END IF;
FETCH col_names
INTO col_name;
SET query1 = query1 + ' WHEN ' + i + ' THEN ' + #tableName + '.' + col_name
SET i = i + 1;
END LOOP the_loop;
SET query1 = query1 + ' END as Answer
FROM survey_questions
JOIN ' + #tableName + '
ON survey_questions.qid = ' + #tableName + '.id
AND survey_questions.gid = ' + #tableName + '.token_id
WHERE survey_questions.sid = ' + surveyId;
SET #Sql = query1;
PREPARE STMT FROM #Sql;
EXECUTE STMT;
DEALLOCATE PREPARE STMT;
END