Concat is not working in mysql inclause - mysql

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.

Related

MySql - using dynamic table names in one query

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

MySQL how to form query

I have 2 mysql tables I'm working with.
Table 1 has employee information columns :
employee_number, date_of_birth, name, address, etc.
Table 2 has pay information columns : employee_number (foreign key), date, bonus
My boss has requested a screen to show the data like, with dates listed horizontally across the top and employee listed vertically :
1/1/2000 | 1/1/2001 | 1/1/2002 | 1/1/2003
Bill $500 | $600 | $700 | $900
Ferdie $300 | $500 | $800 | $434
Tony $450 | $234 | $432 | $343
What is the easiest way to format my query so that the dataset is in this format?
What you need is a pivot table.
MySQL does not have a built in way to create a pivot table, but you can create it "by hand":
SQL Fiddle
MySQL 5.6 Schema Setup:
create table employees(
employee_number int primary key,
name varchar(50)
);
create table payments(
employee_number int,
bonus_date date,
bonus decimal(8,2)
);
insert into employees values
(1, 'Bill'), (2, 'Freddie'), (3, 'Tony');
insert into payments values
(1, '2000-1-1',500),(1, '2001-1-1',600),(1, '2002-1-1',700),(1, '2003-1-1',900),
(2, '2000-1-1',300),(2, '2001-1-1',500),(2, '2002-1-1',800),(2, '2003-1-1',434),
(3, '2000-1-1',450),(3, '2001-1-1',234),(3, '2002-1-1',432),(3, '2003-1-1',343);
Query 1:
-- 1. Prepare the column definition
select group_concat(distinct
concat(
"sum(case when bonus_date='", bonus_date, "' then bonus else 0 end) as `", bonus_date, "`"
)
)
into #sql
from payments
Results: No results
Query 2:
-- 2. Write the full query
set #sql = concat("select e.employee_number, e.name, ", #sql, " from employees as e inner join payments as p on e.employee_number = p.employee_number group by e.employee_number")
Results: No results
Query 3:
-- 3. Check the generated query (optional)
-- select #sql
Results: (uncomment the above query and check the result)
Query 4:
-- 4. Create a prepared statement using the query you've just created
prepare stmt from #sql
Results: No results
Query 5:
-- 5. Execute the prepared statement
execute stmt
Results:
| employee_number | name | 2000-01-01 | 2001-01-01 | 2002-01-01 | 2003-01-01 |
|-----------------|---------|------------|------------|------------|------------|
| 1 | Bill | 500 | 600 | 700 | 900 |
| 2 | Freddie | 300 | 500 | 800 | 434 |
| 3 | Tony | 450 | 234 | 432 | 343 |
Query 6:
-- 6. When you're done, deallocate the prepared statement
deallocate prepare stmt
Results: No results
You may want to check my answer on a similar question.
Hope this helps.

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;

MySQL accessing two tables in single query

Here's a simplified version of my troubles. 3 tables, the first (transit) will be used in upcoming procedures and functions, the second (products) will hold stationary data about products, the third (userWatchList) will hold user-specific data related to products.
TABLE: transit
+---------+------+
| ranking | data |
+---------+------+
| | |
+---------+------+
TABLE: products
+----+------+-----------------+
| ID | data | importantnumber |
+----+------+-----------------+
| 1 | c | 10 |
| 2 | u | 20 |
| 3 | t | 20 |
| 4 | u | 40 |
+----+------+-----------------+
TABLE: userWatchList
+---------+----+
| ranking | ID |
+---------+----+
| 1 | 2 |
| 2 | 1 |
| 3 | 4 |
| 4 | 3 |
+---------+----+
I need to insert into "transit" the data and ranking of rows that are within the needed ranking range and the data of which meets certain requirements.
I now want the ranking and data of a product, that has an importantnumber value of 20.
Say the allowed ranking range was between 1 and 2, SELECT * FROM transit at the end of the desired process would output:
+---------+------+
| ranking | data |
+---------+------+
| 1 | 'u' |
+---------+------+
Say the allowed ranking range was between 1 and 3, SELECT * FROM transit at the end of the desired process would output:
+---------+------+
| ranking | data |
+---------+------+
| 1 | 'u' |
| 4 | 't' |
+---------+------+
My vision of a possible solution...
To make sure the ranking falls within the needed range, I thought I might use dynamic SQL:
SET #IDsRetrieveStmt = CONCAT("SELECT group_concat(ID SEPARATOR ',') INTO #IDsStr FROM userWatchList WHERE ranking BETWEEN ', #rankingmin,' AND ', #rankingmax,';');
PREPARE stmt FROM #IDsRetrieveStmt;
EXECUTE stmt;
Now. To add ranking value to those fields... what should i do? I imagine one option is somewhere along the lines of:
SET #fetch_data_stmt = CONCAT('INSERT INTO transit (data, ranking) SELECT data, ( **** ) FROM products WHERE ID IN ( ', #IDsStr, ') AND importantnumber=20;');
PREPARE stmt FROM #fetch_data_stmt;
EXECUTE stmt;
** some unknown magic here that fetches ranking from a row with the same ID from 'products' table. This could be SELECT ranking FROM userWatchList WHERE ID=ID, but as you see, the ID part will probably create a conflict. Also, it seems a bit ineffective to run a new SELECT query with every inserted row.
I am sure there is a more effective way of doing this that I haven't heard of yet.
What's the best way of achieving this? Thanks in advance!
The first, and most important, part of the answer is the query that generates the data you want. You need to join the two tables together and use your criteria as conditions in the query:
select ranking, data
from userWatchList u
join product p on p.ID = u.ID
where ranking between ? and ?
and importantnumber = ?
Of course substituting ? with your criteria.
The next part of the answer is more advice. Unless there's an extremely compelling reason to do so, don't create a table to hold the data output from this query, because it's derived data that is out of date the instant it's created, unless you put in complicated database infrastructure (triggers) to keep it fresh.
Instead, create a view, that's like a table to a client (an application), but is actually a query under the hood:
create view transit as
select ranking, data, importantnumber
from userWatchList u
join product p on p.ID = u.ID
Then to use:
select ranking, data
from transit
where ranking between ? and ?
and importantnumber = ?

How to get the records according to the where clauses in Mysql?

I have been facing a small confusion from which I am not able to get rid.
I have a table "user_questions" with the attributes id, question_sequence_number, title. From that table I want to take the records according to the priority given like first I have given 'question_sequence_number' and then 'id'. So what I want is that the records should be displayed first with the sequence numbers I have given and the records should be displayed next with Ids I have given.
For that I have written a mySql query something like follows:
SELECT "user_questions".* FROM "user_questions" WHERE (question_sequence_number IN (11,13,16,19) OR id IN (198,199,200,201,202))
But the records are coming first with ids I have given and the remaining records with question sequence number.
Can anybody please help me out in this to achieve ?
Thanks.
To get the order of records you need you have to use ORDER BY clause. Otherwise the order is not guaranteed.
That being said you can try something like this
SELECT *
FROM user_questions
WHERE question_sequence_number IN (11,13,16,19)
OR id IN (198,199,200,201,202)
ORDER BY (question_sequence_number IN (11,13,16,19)) DESC,
CASE WHEN question_sequence_number IN (11,13,16,19)
THEN question_sequence_number
ELSE id
END
Output:
| ID | QUESTION_SEQUENCE_NUMBER |
|-----|--------------------------|
| 2 | 11 |
| 4 | 13 |
| 3 | 16 |
| 1 | 19 |
| 198 | 5 |
| 199 | 4 |
| 200 | 3 |
| 201 | 2 |
| 202 | 1 |
Here is SQLFiddle demo
DECLARE _Str VARCHAR(500);
SET _Str=CONCAT("SELECT user_questions.* FROM user_questions WHERE (question_sequence_number IN ("11,13,16,19") OR id IN ("198,199,200,201,202")")
PREPARE stmt FROM #s;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;