msql insert data into table based on values in two other tables - mysql

I am using MySQL and have a table (documentShips) that I want to store connections / links between documents and users.
The users table has columns including id, first_name and last_name etc...
The documents table has columns including id and users, where the users column contains a comma separated value
E.g. "Joe Bloggs, Fred Nerk, Simon McCool" etc...
I want to match users between the tables (documents and users) using a like statement, e.g.:
where documents.authors like '% users.last_name %'
and insert them into the documentShips table, e.g.:
insert into documentShips (user_id, document_id) ... values () ... where ...
I am struggling to create a valid (mysql) insert statement to do this.
Any help would be greatly appreciated !!!
Thanks,
Jon.

If I understand correctly you can use FIND_IN_SET() like this
INSERT INTO documentShips (user_id, document_id)
SELECT u.id, d.id
FROM documents d JOIN users u
ON FIND_IN_SET(CONCAT(u.first_name, ' ', u.last_name), d.authors) > 0
ORDER BY d.id, u.id
Here is SQLFiddle demo
In order for it to work correctly you have to make sure that comma separated values in document.authors have no spaces before or after commas. If in fact you have spaces then eliminate them first with a query like this
UPDATE documents
SET authors = REPLACE(REPLACE(authors, ' ,', ','), ', ', ',');
Here is SQLFiddle demo
Now consider to normalize your documents table.

Use INSERT...SELECT syntax as shown in the MySQL documentation. The documentation also has some examples.

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

Mysql show other different cell data with under group by same name

Let's assume I have a table which store register user data, the records might have same registered name but different email, like following:
I want to create a front view to manipulate those data but I don't want those same name show repeatedly, can mysql statement query to output result like
this is the result so far I can do but it can't bind same name into one.
select * from `register`
where `fullname` in (
select `fullname` from `register`
group by `fullname` having count(*) > 1
)
One thing you could do is to do a SELECT DISTINCT on the duplicate row, and make use of the GROUP_CONCAT(); function in MYSQL to concatenate your desired values into one row, and GROUP BY fullname to get the order you wanted.
Note that I am also putting the user ids into a grouped row, so that you can track which ids belong to which name.
SELECT
DISTINCT fullname as full_name,
GROUP_CONCAT(id SEPARATOR ', ') as user_ids,
GROUP_CONCAT(email SEPARATOR ', ') as emails
FROM
tbl_register
GROUP BY
tbl_register.fullname
Working SQL Fiddle
This would be the logical way to do it. Hope this helped. :)
More information on the GROUP_CONCAT(); function here: https://dev.mysql.com/doc/refman/8.0/en/group-by-functions.html#function_group-concat
Try this:
SELECT DISTINCT *duplicate_column* FROM *table_name1* WHERE *col_id* IN (SELECT *cols_to_dusplay* FROM *table_name1* GROUP_BY *duplicate_column*

Query for first result in a column - substring_index function - MySQL

I can't seem to get the substring_index() to work:
I have created a simple table as follows:
CREATE TABLE ContactList(
cont_id int(11) NOT NULL AUTO_INCREMENT,
last_name varchar(30),
first_name varchar(20),
interests varchar(100),
PRIMARY KEY(cont_id));
I then populated the ContactList table as follows:
INSERT INTO ContactList (last_name, first_name, interests)
VALUES
('Murphy', 'Dave', 'Golf, Pets, Basketball'),
('Murphy', 'Ben', 'Pets, Gym, Basketball'),
('Finn', 'Belinda', 'Pets, Tennis, Knitting'),
('Murphy', 'Steve', 'Pets, Archery, Fishing');
I ran a quick SELECT to ensure the data was entered correctly:
SELECT * FROM ContactList;
Then I ran the following query:
SELECT * FROM ContactList
WHERE last_name = 'Murphy'
AND SUBSTRING_INDEX(interests, ',' ,1) = 'Pets';
I was expecting to get two records back (which I did for Ben & Steve), however, for the 'Interests' column I was assuming I should only get one interest back if it equaled 'pets' (due to the substring_index) however, I got all interests back. How can I use the SUBSTRING_INDEX() to run the query and only get the first interest listed back for each record if it says 'Pets'?
BTW I am using MySQL Version 5.5.24 and I know the Interests would be best suited in their own table - I just want to see why substring_index is not picking the first item from the list if it equals 'pets'.
Thanks for any input,
Andy R ;-)
You're using SUBSTRING_INDEX in the WHERE clause, which determines which rows to include. That's good, but you also need to use it in the SELECT clause, which determines which columns to include.
Try this:
SELECT
last_name,
first_name,
SUBSTRING_INDEX(interests, ',' ,1) AS FirstInterestInList
FROM ContactList
WHERE last_name = 'Murphy'
AND SUBSTRING_INDEX(interests, ',' ,1) = 'Pets';
Although substring_index() will work for the first element, you really want find_in_list():
SELECT last_name, first_name, SUBSTRING_INDEX(interests, ',' ,1) AS FirstInterestInList
FROM ContactList
WHERE last_name = 'Murphy' and
find_in_set('pets', interests) = 1
The advantage of find_inset() is that it will work for arbitrary positions.
Just as a note, though, your delimiter is ', '. For find_in_set() to work best, you should have no space after the column.
Also, if you are doing queries like this, you should fix your data structure. It really wants a table called something like ContactInterests which contains one row for each contact and each interest.

MYSQL explode search

I have a field that is build like this "1;2;3;4;8;9;11;"
If I want to search if a number is in this range I do it like this:
SELECT * FROM table WHERE [id] LIKE '[number];%' OR '%;[number];%'
Is there another more easy way where i can split the string?
Many thanks
If you are storing the values in a string, the best way to use like is as:
SELECT *
FROM table
WHERE concat(';', #numbers) like concat('%;', [id], ';%')
MySQL also offers find_in_set() when the delimiter is a comma:
SELECT *
FROM table
WHERE find_in_set(id, replace(#numers, ';', ',')
Use IN() with a comma delimited string of IDs
SELECT * FROM table WHERE id IN(1,2,3,4,8,9,11)
create another table called helper with 2 fields: number and id. you will need to programmatically create it by splitting the id field and add one row to this table for each number in each id.
create an index this table on number
select table.* from table where id in (select id from helper where number = 23)
you can use this
SELECT name ,`from`, find_in_set('4' , REPLACE(`from`, ';', ',')) as the_place_of_myval FROM reservation
having the_place_of_myval != 0
note: that in my query im searching for strings which have number 4
demo here in my demo there is two examples.

How to count items in comma separated list MySQL

So my question is pretty simple:
I have a column in SQL which is a comma separated list (ie cats,dogs,cows,) I need to count the number of items in it using only sql (so whatever my function is (lets call it fx for now) would work like this:
SELECT fx(fooCommaDelimColumn) AS listCount FROM table WHERE id=...
I know that that is flawed, but you get the idea (BTW if the value of fooCommaDelimColumn is cats,dogs,cows,, then listCount should return 4...).
That is all.
There is no built-in function that counts occurences of substring in a string, but you can calculate the difference between the original string, and the same string without commas:
LENGTH(fooCommaDelimColumn) - LENGTH(REPLACE(fooCommaDelimColumn, ',', ''))
It was edited multiple times over the course of almost 8 years now (wow!), so for sake of clarity: the query above does not need a + 1, because OPs data has an extra trailing comma.
While indeed, in general case for the string that looks like this: foo,bar,baz the correct expression would be
LENGTH(col) - LENGTH(REPLACE(col, ',', '')) + 1
zerkms' solution works, no doubt about that. But your problem is created by an incorrect database schema, as Steve Wellens pointed out. You should not have more than one value in one column because it breaks the first normal law. Instead, you should make at least two tables. For instance, let's say that you have members who own animals :
table member (member_id, member_name)
table member_animal (member_id, animal_name)
Even better: since many users can have the same type of animal, you should create 3 tables :
table member (member_id, member_name)
table animal (animal_id, animal_name)
table member_animal (member_id, animal_id)
You could populate your tables like this, for instance :
member (1, 'Tomas')
member (2, 'Vincent')
animal (1, 'cat')
animal (2, 'dog')
animal (3, 'turtle')
member_animal (1, 1)
member_animal (1, 3)
member_animal (2, 2)
member_animal (2, 3)
And, to answer your initial question, this is what you would do if you wanted to know how many animals each user has :
SELECT member_id, COUNT(*) AS num_animals
FROM member
INNER JOIN member_animal
USING (member_id)
INNER JOIN animal
USING (animal_id)
GROUP BY member_id;
Following the suggestion from #zerkms.
If you dont know if there is a trailing comma or not, use the TRIM function to remove any trailing commas:
(
LENGTH(TRIM(BOTH ',' FROM fooCommaDelimColumn))
- LENGTH(REPLACE(TRIM(BOTH ',' FROM fooCommaDelimColumn), ',', ''))
+ 1
) as count
Reference: http://dev.mysql.com/doc/refman/5.0/en/string-functions.html#function_trim
I also agree that a refactoring of the tables is the best option, but if this is not possible now, this snippet can do the work.
This version doesn't support leading or trailing commas, but supports an empty value with a count of 0:
IF(values, LENGTH(values) - LENGTH(REPLACE(values, ',', '')) + 1, 0) AS values_count
The answer is to correct the database schema. It sounds like a many-to-many relationship which requires a junction table. http://en.wikipedia.org/wiki/Junction_table
If we do +1 and if we have an empty column it always comes as 1 to make it 0 we can use IF condition in mySQL.
IF(LENGTH(column_name) > 0, LENGTH(column_name) - LENGTH(REPLACE(column_name, ',', '')) + 1, 0)