mysql select update - mysql

I have read quite a few selcet+update questions in here but cannot understand how to do it. So will have to ask from the beginning.
I would like to update a table based on data in another table. Setup is like this:
- TABLE a ( int ; string )
ID WORD
1 banana
2 orange
3 apple
- TABLE b ( "comma separated" string ; string )
WORDS TEXTAREA
0 banana -> 0,1
0 orange apple apple -> BEST:0,2,3 ELSE 0,2,3,3
0 banana orange apple -> 0,1,2,3
Now I would like to for each word in TABLE a append ",a.ID" to b.WORDS like:
SELECT id, word FROM a
(for each) -> UPDATE b SET words = CONCAT(words, ',', a.id) WHERE b.textarea like %a.word%
Or even better: replace the word found in b.textarea with ",a.id" so it is the b.textarea that ends up beeing a comma separeted string of id's... But I do not know if that is possible.
Tried this but not working. But I think I am getting closer:
UPDATE a, b
SET b.textarea =
replace(b.textarea,a.word,CONCAT(',',a.id))
WHERE a.word IN (b.textarea)
ORDER BY length(a.word) DESC

I ended up doing a work-a-round. I exported all a.words to excel and created an update for each row like this:
UPDATE `tx_ogarktiskdocarchive_loebe` SET `temp_dictionay` = replace(lower(temp_dictionay) , lower('Drygalski’s Grønlandsekspedition'), CONCAT(',',191));
Then I pasted the aprox 1000 rows into ans sql file and executed it. Done.

I had to do "a cleaner double post" of this one to get the answer.
A solution can be put together based on this manual:
http://dev.mysql.com/doc/refman/5.1/en/group-by-functions.html#function_group-concat
GROUP_CONCAT will make a comma separated string based on the fileds it shall CONCAT. Perfect. And regarding the preferred solution with no dublicates in the result there is this example in the manual that will filter out dublicates using DISTINCT inside the GROUP_CONCAT:
mysql> SELECT student_name,
-> GROUP_CONCAT(DISTINCT test_score
-> ORDER BY test_score DESC SEPARATOR ' ')
-> FROM student
-> GROUP BY student_name;

Related

how to pass multiple variables in WHERE ... IN in stored procedure? [duplicate]

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,%';

SQL Select if substring occurs then copy until substring else keep original

I have a database with TV Guide data, and in my description field (VARCHAR) sometimes i have a '|' where behind it is the rating. I used to check this in php, before converting it all to XML, but i would like to do this in SQL.
So if i have this string:
This is the description | rating pg-13
Then i want to keep the
This is the description
but if there is no '|' i want the whole string.
I tried using substring, but can't get it to work.
My query now is:
SELECT *, SUBSTRING(`long_description`, 1, POSITION('|' IN `long_description`)) FROM `programs` WHERE station_id = 1
this works only one way - this gives me the string before the '|' but if there is no '|' it gives an empty column.
Based on the use of backticks, you might be using MySQL. If so, substring_index() does exactly what you want:
select substring_index(long_description, '|', 1)
How about this:
SELECT
*,
IF(long_description LIKE '%|%',
SUBSTRING(`long_description`,
1,
POSITION('|' IN `long_description`)),
long_description)
FROM
`programs`
WHERE
station_id = 1
The IF clause basically just checks if you have a | in the field and applies your routine when this is true. Else it will simply return the complete long_description value.

Select All Distinct Words in Column MYSQL

I have a column in which is stored nothing but text separated by one space. There may be one to maybe 5 words in each field of the column. I need a query to return all the distinct words in that column.
Tried:
SELECT DISTINCT tags FROM documents ORDER BY tags
but does not work.
To Elaborate.
I have a column called tags. In it I may have the following entries:
Row 1 Red Green Blue Yellow
Row 2 Red Blue Orange
Row 3 Green Blue Brown
I want to select all the DISTINCT words in the entire column - all fields. It would return:
Red Green Blue Yellow Orange Brown
If I counted each it would return:
2 Red
2 Green
3 Blue
1 Yellow
1 Brown
1 Orange
To fix this I ended up creating a second table where all keywords where inserted on their own row each along with a record key that tied them back to the original record in the main data table. I then just have to SELECT DISTINCT to get all tags or I can SELECT DISTINCT with a WHERE clause specifying the original record to get the tags associated with a unique record. Much easier.
There is not a good solution for this. You can achieve this with JSON functions as of 5.6, I think, but it's a little tricky until 8.0, when mySQL added the JSON_TABLE function, which can convert json data to a table like object and perform selects on it, but how it will perform is dependent on your actual data. Here's a working example:
CREATE TABLE t(raw varchar(100));
INSERT INTO t (raw) VALUES ('this is a test');
You will need to strip the symbols (commas, periods, maybe others) from your text, then replace any white text with ",", then wrap the whole thing in [" and "] to json format it. I'm not going to give a full featured example, because you know better than I do what your data looks like, but something like this (in its simplest form):
SELECT CONCAT('["', REPLACE(raw, ' ', '","'), '"]') FROM t;
With JSON_TABLE, you can do something like this:
SELECT CONCAT('["', REPLACE(raw, ' ', '","'), '"]') INTO #delimited FROM t;
SELECT *
FROM JSON_TABLE(
#delimited,
"$[*]"
COLUMNS(Value varchar(50) PATH "$")
) d;
See this fiddle: https://dbfiddle.uk/?rdbms=mysql_8.0&fiddle=7a86fcc77408ff5dfec7a805c6e4117a
At this point you have a table of the split words, and you can replace SELECT * with whatever counting query you want, probably SELECT Value, count(*) as vol. You will also need to use group_concat to handle multiple rows. Like this:
insert into t (raw) values ('this is also a test'), ('and you can test it');
select concat(
'["',
replace(group_concat(raw SEPARATOR '","'), ' ', '","'),
'"]'
) into #delimited from t;
SELECT Value, count(*) as vol
FROM JSON_TABLE(
#delimited,
"$[*]"
COLUMNS(Value varchar(50) PATH "$")
) d
GROUP BY Value ORDER BY count(*) DESC;
If you are running <8.0, you can still accomplish this, but it will take some hackiness, like generating an arbitrary list of numbers and constructing the paths dynamically from that.

mysql: special use of concat

I have table A and B with many to one associations (b contains fk_a). Let's assume the sample tables are as follows:
A:
id first
1 sample
2 sample
B:
id fk_a type value
1 1 som thing
2 1 oth other
3 2 som thing
4 2 oth any
I would like the "first" column in table A to be unique, and I would like to achieve it by having to:
desired A:
id first
1 sample-thing-other
2 sample-thing-any
Is it possible to use pure MYSQL to use UPDATE and CONCAT on table A to obtain desired update?
It would be easy if I had everything in one table, I could just write
UPDATE A
SET first = CONCAT(first, value)
but unfortunately I have many-to-one association and I am not sure if it is even possible in such case.
I do not have the instance of mysql, and not test it. use the group_concat
UPDATE A, (SELECT fk_a, GROUP_CONCAT(value SEPARATOR '-') as concat_value FROM B GROUP BY fk_a) AS t
SET A.first = CONCAT(A.first, '-', t.concat_value)
WHERE A.id = t.fk_a;
Group_Concat is your friend.
But may I remind you of first normal form?

Strip out digits/numeric chars from a mysql string column

I have columns in a mysql table that stores names of people as combinations of strings and incremented digits for uniqueness, so I have names stored as so :
Patrick, Patrick1, Patrick2, ..... Patrick10, David, David2, .... David5
How do I retrieve just the alpha name itself, without the digits? Say I want to group by the distinct names, and count per group, so I get a result resembling the following.
name | frequency
-----------------
Patrick | 10
David | 5
A solution would be this:(it doesn't look to good, but it works)
SELECT
TRIM(TRAILING '0' FROM
TRIM(TRAILING '1' FROM
TRIM(TRAILING '2' FROM
TRIM(TRAILING '3' FROM
-- ...
TRIM(TRAILING '8' FROM
TRIM(TRAILING '9' FROM name)))))) AS name
FROM your_table
Then you can select with GROUP BY from the result:
SELECT name, count(*) AS frequency FROM (
-- previous select
) AS t
GROUP BY name
I'll have a little think about that, but I would recommend that if you need a distinguishing number, you keep it in a different column. That way, you won't have difficulties of this sort.
You can "chain" the replace command like this (this will remove the digits 0,1,2 in the query). You can expand this for the other digits, but I don't know if this will perform very well on large datasets:
select replace(replace(replace(Name,"0",""),"1",""),"2","") from users;
I would think also, it will be better to do what Brian suggested.
you could use a udf.
and then try Something like follwing
select REGEX_REPLACE(name, [0-9], '') as Name, Count(Name)
from tableName
Group by Name