I am wanting to move items from one table into another using MySQL.
This is what I currently use:
INSERT INTO items_rooms (item_id, room_id,x,y,n)
SELECT id, room_id,x,y,z
FROM `items_phoenix`
This works, however I am wanting to insert multiple values into one column instead of each values in different columns.
For example:
In table items_rooms, I would like values x and y from items_phoenix to be placed into one column in items_rooms.
If x = 5 and y = 2, can I save it into items_rooms like this: one column: 5.2 instead of different columns for each values?
Sorry if this is confusing!
You can use expressions in your SELECT column-list. As long as the columns in the select-list and the columns in the destination table match in number and data type, you can do something like this:
INSERT INTO items_rooms (item_id, room_id, x_dot_y, n)
SELECT id, room_id, CONCAT(x,'.',y), z
FROM `items_phoenix`
You definitely can do this. See the helpful answers on this. However, you should consider whether this is a good idea. Once you denormalize distinct columns into some kind of string expression in one column, you've created something that is very difficult to maintain and also to query against. You are making your data less usable to yourself in doing this.
You can use a concatenation for a simple case, e.g:
INSERT INTO items_rooms (
item_id,
room_id,
x_and_y,
n)
SELECT
id,
room_id,
CONCAT(x, '.', y),
z
FROM `items_phoenix`
Yes, if the column can store text information
Just CONCAT or CONCAT_WS the values
INSERT INTO items_rooms ( item_id, room_id, my_value, n )
SELECT id, room_id, CONCAT_WS( '.', x, y ), z
FROM `items_phoenix`
SQL Fiddle DEMO
$table_To_Get_From = mysql_query("SELECT * FROM items_phoenix (id, room_id,x,y,z)");
if($table_To_Get_From){
$table = mysql_fetch_assoc($table_To_Get_From);
$values = array($table['id'],$table['room_id'],$table['x'],$table['y'],$table['z']);
mysql_query("INSERT INTO items_rooms (item_id, room_id,x,y,n)
VALUES ($values[0],$values[1],$values[2],$values[3],$value[4])");
}
I didn't make them strings, so you may need to use '$values[num]'
This will insert it like you wanted, if there is any values.
Related
I have a column in one of my table where I store multiple ids seperated by comma's.
Is there a way in which I can use this column's value in the "IN" clause of a query.
The column(city) has values like 6,7,8,16,21,2
I need to use as
select * from table where e_ID in (Select city from locations where e_Id=?)
I am satisfied with Crozin's answer, but I am open to suggestions, views and options.
Feel free to share your views.
Building on the FIND_IN_SET() example from #Jeremy Smith, you can do it with a join so you don't have to run a subquery.
SELECT * FROM table t
JOIN locations l ON FIND_IN_SET(t.e_ID, l.city) > 0
WHERE l.e_ID = ?
This is known to perform very poorly, since it has to do table-scans, evaluating the FIND_IN_SET() function for every combination of rows in table and locations. It cannot make use of an index, and there's no way to improve it.
I know you said you are trying to make the best of a bad database design, but you must understand just how drastically bad this is.
Explanation: Suppose I were to ask you to look up everyone in a telephone book whose first, middle, or last initial is "J." There's no way the sorted order of the book helps in this case, since you have to scan every single page anyway.
The LIKE solution given by #fthiella has a similar problem with regards to performance. It cannot be indexed.
Also see my answer to Is storing a delimited list in a database column really that bad? for other pitfalls of this way of storing denormalized data.
If you can create a supplementary table to store an index, you can map the locations to each entry in the city list:
CREATE TABLE location2city (
location INT,
city INT,
PRIMARY KEY (location, city)
);
Assuming you have a lookup table for all possible cities (not just those mentioned in the table) you can bear the inefficiency one time to produce the mapping:
INSERT INTO location2city (location, city)
SELECT l.e_ID, c.e_ID FROM cities c JOIN locations l
ON FIND_IN_SET(c.e_ID, l.city) > 0;
Now you can run a much more efficient query to find entries in your table:
SELECT * FROM location2city l
JOIN table t ON t.e_ID = l.city
WHERE l.e_ID = ?;
This can make use of an index. Now you just need to take care that any INSERT/UPDATE/DELETE of rows in locations also inserts the corresponding mapping rows in location2city.
From MySQL's point of view you're not storing multiple ids separated by comma - you're storing a text value, which has the exact same meaing as "Hello World" or "I like cakes!" - i.e. it doesn't have any meaing.
What you have to do is to create a separated table that will link two objects from the database together. Read more about many-to-many or one-to-many (depending on your requirements) relationships in SQL-based databases.
Rather than use IN on your query, use FIND_IN_SET (docs):
SELECT * FROM table
WHERE 0 < FIND_IN_SET(e_ID, (
SELECT city FROM locations WHERE e_ID=?))
The usual caveats about first form normalization apply (the database shouldn't store multiple values in a single column), but if you're stuck with it, then the above statement should help.
This does not use IN clause, but it should do what you need:
Select *
from table
where
CONCAT(',', (Select city from locations where e_Id=?), ',')
LIKE
CONCAT('%,', e_ID, ',%')
but you have to make sure that e_ID does not contain any commas or any jolly character.
e.g.
CONCAT(',', '6,7,8,16,21,2', ',') returns ',6,7,8,16,21,2,'
e_ID=1 --> ',6,7,8,16,21,2,' LIKE '%,1,%' ? FALSE
e_ID=6 --> ',6,7,8,16,21,2,' LIKE '%,6,%' ? TRUE
e_ID=21 --> ',6,7,8,16,21,2,' LIKE '%,21,%' ? TRUE
e_ID=2 --> ',6,7,8,16,21,2,' LIKE '%,2,%' ? TRUE
e_ID=3 --> ',6,7,8,16,21,2,' LIKE '%,3,%' ? FALSE
etc.
Don't know if this is what you want to accomplish. With MySQL there is feature to concatenate values from a group GROUP_CONCAT
You can try something like this:
select * from table where e_ID in (Select GROUP_CONCAT(city SEPARATOR ',') from locations where e_Id=?)
this one in for oracle ..here string concatenation is done by wm_concat
select * from table where e_ID in (Select wm_concat(city) from locations where e_Id=?)
yes i agree with raheel shan .. in order put this "in" clause we need to make that column into row below code one do that job.
select * from table where to_char(e_ID)
in (
select substr(city,instr(city,',',1,rownum)+1,instr(city,',',1,rownum+1)-instr(city,',',1,rownum)-1) from
(
select ','||WM_CONCAT(city)||',' city,length(WM_CONCAT(city))-length(replace(WM_CONCAT(city),','))+1 CNT from locations where e_Id=? ) TST
,ALL_OBJECTS OBJ where TST.CNT>=rownum
) ;
you should use
FIND_IN_SET Returns position of value in string of comma-separated values
mysql> SELECT FIND_IN_SET('b','a,b,c,d');
-> 2
You need to "SPLIT" the city column values. It will be like:
SELECT *
FROM table
WHERE e_ID IN (SELECT TO_NUMBER(
SPLIT_STR(city /*string*/
, ',' /*delimiter*/
, 1 /*start_position*/
)
)
FROM locations);
You can read more about the MySQL split_str function here: http://blog.fedecarg.com/2009/02/22/mysql-split-string-function/
Also, I have used the TO_NUMBER function of Oracle here. Please replace it with a proper MySQL function.
IN takes rows so taking comma seperated column for search will not do what you want but if you provide data like this ('1','2','3') this will work but you can not save data like this in your field whatever you insert in the column it will take the whole thing as a string.
You can create a prepared statement dynamically like this
set #sql = concat('select * from city where city_id in (',
(select cities from location where location_id = 3),
')');
prepare in_stmt from #sql;
execute in_stmt;
deallocate prepare in_stmt;
Ref: Use a comma-separated string in an IN () in MySQL
Recently I faced the same problem and this is how I resolved it.
It worked for me, hope this is what you were looking for.
select * from table_name t where (select (CONCAT(',',(Select city from locations l where l.e_Id=?),',')) as city_string) LIKE CONCAT('%,',t.e_ID,',%');
Example: It will look like this
select * from table_name t where ',6,7,8,16,21,2,' LIKE '%,2,%';
Below is the table of animals on various floors.
ID,FLOOR_LEVEL,ANIMAL [column names]
01,A,CAT
02,A,DOG
03,B,DOG
04,B,CAT
05,B,CAT
06,C,CAT
I want to label the types of animal(i.e cat will be labelled as 1, dog will be labelled as 2....) like shown below by creating a new column LABEL.
ID,FLOOR_LEVEL,ANIMAL,LABEL [column names]
01,A,CAT,1
02,A,DOG,2
03,B,DOG,2
04,B,CAT,1
05,B,CAT,1
06,C,CAT,1
It can be done by writing query such as
INSERT INTO table_name (LABEL)
VALUES (1,2,2,1,1,1);
But, how can this be generalised for a huge no. of different type of animals in MySQL by writing query? Please help.
Your INSERT statement makes no sense:
INSERT INTO table_name (LABEL) VALUES (1,2,2,1,1,1);
Will insert 1 row into a table called table_name with 6 columns. It would do nothing for you.
Instead make a new table to store the animal id and animal name:
CREATE TABLE animals ( id int, animal_name VARCHAR(50));
INSERT INTO animals VALUES (1, 'cat'),(2, 'dog'),(3,'tardigrade'),(4,'liger');
And then join that into your query:
SELECT t1.floor_level, t1.animal, t2.id
FROM table t1
INNER JOIN animals t2 ON
t1.animal = t2.animal_name;
Optionally you could use a case statement to do this within the query. It will get a little laborious if you have a ton of animals though. And you will have to rewrite it every time you query this table.
SELECT floor_level,
animal,
CASE WHEN animal = 'cat' THEN 1
WHEN animal = 'dog' THEN 2
WHEN animal = 'tardigrade' THEN 3
WHEN animal = 'liger' THEN 4
END as animal_id
FROM table;
If your requirement is not a numeric label, try using md5() to generate a hash label for the animals. Its value would be same for animals with same name (case sensitive).
SELECT ID, FLOOR_LEVEL, ANIMAL, MD5(UPPER(ANIMAL))
FROM table;
Please note that MD5() string are case sensitive. i.e. DOG and Dog would generate a different output, hence I have used UPPER() method.
Another option is to use below function, which will convert the alphanumeric md5() value to a long type. But the numeric value would not be starting from 1, 2 etc.
cast(conv(substring(md5(upper(animal)), 1, 16), 16, 10) as unsigned integer)
EDIT - Based on OP comment
But is there any way to reduce the length of generated values?
To reduce the length of the generated value, Use rand() function wisely to reduce the length of the integer. The only concern is that you may not want a collision of generated values.
round(cast(conv(substring(md5(upper(animal)), 1, 16), 16, 10) as unsigned integer) * (rand() * rand()))
A more sophisticated, optimized and non-colliding value may be generated using Hive UDF.
The simplest way to solve your problem is with a DECODE
SELECT
DECODE(
ANIMAL,
'CAT',1,
'DOG',2,
'OTHER'
)
FROM ...
Here is more info:
https://docs.oracle.com/cd/B19306_01/server.102/b14200/functions040.htm
Good Luck!
The command you are looking for is CASE
SELECT ID, FLOOR_LEVEL, ANIMAL,
CASE
WHEN ANIMAL = 'CAT' THEN 1
WHEN ANIMAL = 'DOG' THEN 2
END
FROM table;
I am looking to compare the results of 2 cells in the same row. the way the data is structured is essentially this:
Col_A: table,row,cell
Col_B: row
What I want to do is compare when Col_A 'row' is the same as Col_B 'row'
SELECT COUNT(*) FROM MyTable WHERE Col_A CONTAINS Col_B;
sample data:
Col_A: a=befd-47a8021a6522,b=7750195008,c=prof
Col_B: b=7750195008
Col_A: a=bokl-e5ac10085202,b=4478542348,c=pedf
Col_B: b=7750195008
I am looking to return the number of times the comparison between Col_A 'b' and Col_B 'b' is true.
This does what I was looking for:
SELECT COUNT(*) FROM MyTable WHERE Col_A LIKE CONCAT('%',Col_B,'%');
I see You answered Your own question.
SELECT COUNT(*) FROM MyTable WHERE Col_A LIKE CONCAT('%',Col_B,'%');
is good from performance perspective. While normalization is very good idea, it would not improve speed much in this particular case. We must simply scan all strings from table. Question is, if the query is always correct. It accepts for example
Col_A: a=befd-47a8021a6522,ab=7750195008,c=prof
Col_B: b=7750195008
or
Col_A: a=befd-47a8021a6522,b=775019500877777777,c=prof
Col_B: b=7750195008
this may be a problem depending on the data format. Solution is quite simple
SELECT COUNT(*) FROM MyTable WHERE CONCAT(',',Col_A,',') LIKE CONCAT('%,',Col_B,',%');
But this is not the end. String in LIKE is interpreted and if You can have things like % in You data You have a problem. This should work on mysql:
SELECT COUNT(*) FROM MyTable WHERE LOCATE(CONCAT(',',Col_B,','), CONCAT(',',Col_A,','))>0;
SELECT * FROM MyTable WHERE Col_A = Col_B (AND Col_A = 'cell')
^^ Maybe you are looking for this statement. The part in brackets is optional.
If this is not the solution, please supply us with further information.
The easiest way would be to use the IN operator.
SELECT COUNT(*) FROM MyTable WHERE Col_A IN (Col_B);
More info on the IN operator: http://www.w3schools.com/sql/sql_in.asp
There's also the SUBSTRING() or MID() (depending on what you're using) function if you know that the substring will be in the same position everytime.
MID()/SUBSTRING() function: http://www.w3schools.com/sql/sql_func_mid.asp
You can use SUBSTRING_INDEX to extract a delimited field from a column.
SELECT COUNT(*)
FROM MyTable
WHERE Col_B = SUBSTRING_INDEX(SUBSTRING_INDEX(Col_A, ',', 2), ',', -1)
You need to call it twice to get a single field. The inner call gets the first two fields, the outer call gets the last field of that.
Note that this will be very slow if the table is large, because it's not possible to index substrings in MySQL. It would be much better if you normalized your schema so each field is in a separate column.
If column Col_a has data with format table,row,cell then search expression will be next:
SELECT COUNT(*) FROM MyTable AS MT
WHERE SUBSTRING(Col_A,
INSTR(Col_A, ',b=') + 3,
INSTR(Col_A, ',c=') - INSTR(Col_A, ',b=') + 3) = Col_B
I'm trying do something like this:
SELECT * FROM table WHERE column IN (1,2,3)
but in place of 1,2,3 I want to use a column from another table that contains a comma-delimited list just like "1,2,3" above.
I have tried to do this:
SELECT
GROUP_CONCAT(a.eating_area SEPARATOR ', ')
FROM table_areas a
WHERE a.eating_area_id IN (
SELECT
o.eating_area_ids
FROM table_offers o WHERE o.rid=1
)
however this only returns the value associated with 1, and not 2 or 3. Can this be done or is there another way to do this?
Many thanks
SELECT * FROM table t
WHERE IF(FIND_IN_SET(column,(SELECT "1,2,3" FROM otherTable WHERE 1))>=1,1,0)
-- FIND_IN_SET will return the position.
I don't know if it's the best way to do it but... i think it could work.
Source: Find_in_set
I'm having trouble finding a better way to search MySQL for a pair of values in a table. I have the value pairs in an array, and would like to duplicate the IN() function, but for more than 1 value.
For example purposed; I have the following 3 pairs:
foo,1
boo,2
goo,3
The current solution puts me at:
SELECT * FROM [table] WHERE
(column1 = 'foo' AND column2 = 1) OR
(column1 = 'boo' AND column2 = 2) OR
(column1 = 'goo' AND column2 = 3);
I'd like to think there's a more "sexy" solution seeing that I could have as many as a hundred pairs and having that may ORs kind of makes me nauseous. Thanks!!!
SELECT *
FROM foo
WHERE (column1, column2) IN (('foo', 1), ('bar', 2))
This syntax may be confusing, and it may be more readable to replace it with:
SELECT *
FROM foo
WHERE ROW(column1, column2) IN (ROW('foo', 1), ROW('bar', 2))
I'm used to the former one, though :)
If you can get your values into a temp table (you only need the two columns) easily and quickly, you can just INNER JOIN your way there. If not, you'll have to use #Quassnoi version.
Great answers from #Quassnoi and #KM !!!
Also, you can get pairs duplicates and select them for post-processing:
SELECT *
FROM `foo`
WHERE (`id_obj` , `Foo_obj`)
IN (
SELECT `id_obj` , `Foo_obj`
FROM `foo`
GROUP BY `id_obj` , `Foo_obj`
HAVING count(*) > 1
)