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)
Related
Good Evening Everyone;
I have a table in MySql that I would like to update as follows. Just as an FYI I have searched this topic and have yet to find a solution.
The table name is ALK_Results_NEW in a MySQl database, the data in the ICD9 column contains data seperated by commas.
I need to keep the first set in the ICD9 column so for example the first row has V57.9 , 246.9.
I need to keep the V57.9 in the ICD9 column and move 246.9 into the ICD9_SECONDARY column.
If a row has more than 2 then I need to move all other into the ICD9_OTHER column (This column can have mulitple ICD9 Codes separated by commas)
To summarize the first code needs to stay in the ICD9 column and the second set of codes needs to be moved into the ICD9_SECONDARY. After the data is moved the ICD9 Column should only have the forst set of codes.
Any help would be greatly appreciated.
Assuming the two columns already exist in the table, you can change the data using an update:
update alk_results_new
set icd9_secondary = substr(icd9, instr(icd9, ',') + 1),
icd9 = substring_index(icd9, ',', 1)
where icd9 like '%,%';
EDIT:
Oops, I didn't realize there were three columns. The approach is similar, but a little more complicated because you need to take into account the length of the strings. I think the following should do what you want:
update alk_results_new
set icd9_other = (case when icd9 like '%,%,%'
then substr(icd9, length(substring_index(icd9, ',', 2)) + 2)
end),
icd9_secondary = (case when icd9 like '%,%'
then substring_index(substring_index(icd9, ',', 2), ',', -1)
end),
icd9 = substring_index(icd9, ',', 1);
Note: test the logic out on a select before running the update.
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.
I have the following imported to TableA, Column 'Clothes' and Column 'Colours'
The problem is the import has put in the 'Clothes' column 'Jeans - Blue' and 'Jumper - Red' etc etc
Please could someone help me with a query to keep everything before the - in 'Clothes' and everything after the - into 'Colours' and removing the - altogether.
Two steps for this.
First, update the colors:
UPDATE yourTableA T
SET T.Colours = TRIM(SUBSTR(T.Clothes,INSTR(T.Clothes,'-') + 2));
Second, update the Clothes:
UPDATE yourTableA T
SET T.Clothes = TRIM(SUBSTR(T.Clothes,1,INSTR(T.clothes,'-')-1));
I've used SUBSTR as my string swiss army knife here, and INSTR to locate the position of the - in between. You can do without TRIM, but I usually use this in those cases to avoid unnecessary white spaces.
There surely are more direct ways to do it, but this'll work.
The SUBSTRING_INDEX function is convenient, and the TRIM function can remove leading and trailing spaces. For example:
SELECT TRIM(SUBSTRING_INDEX(a.Clothes,'-',1)) AS Clothes
, TRIM(SUBSTRING_INDEX(a.Clothes,'-',-1)) AS Colours
FROM TableA a
WHERE LENGTH(a.Clothes)-LENGTH(REPLACE(a.Clothes,'-','')) = 1
(NOTE: the query above is returning the substring before the first '-' character, and is returning the substring after the last '-' character. So any values with more than one dash would lose the portion between the first and last dashes, consider e.g. 'A - B - C - D', the query above returns the A and returns the D, and loses everything else.
To handle this anomaly, the WHERE clause checks that the string contains a single occurrence of the '-' character.
Once you have a query you are happy with, you can turn that into an UPDATE statement, BUT be VERY careful about the order you assign new values to columns. Unlike other relational databases, MySQL does not guarantee that a reference to an existing column within the statement will be the value of the column from the beginning of the statement... the only guarantee is that it will be the value that is currently assigned. So, the order that the columns is assigned is important!
UPDATE TableA a
SET Colours = TRIM(SUBSTRING_INDEX(a.Clothes,'-',-1))
, Clothes = TRIM(SUBSTRING_INDEX(a.Clothes,'-',1))
WHERE LENGTH(a.Clothes)-LENGTH(REPLACE(a.Clothes,'-','')) = 1
Note that if we were to assign the Clothes column before we assigned a value to the Colours column, the value we want assigned to Colours would be "lost".
You can do it in a single UPDATE as follows:
UPDATE TableA
SET `Colours` = SUBSTRING_INDEX(`Clothes`, ' - ', -1),
`Clothes` = SUBSTRING_INDEX(`Clothes`, ' - ', 1)
;
You can experiment with SQL Fiddle Demo I created from your data.
Here's the data I worked with:
CREATE TABLE TableA
(Clothes varchar(20), Colours varchar(20))
;
INSERT INTO TableA
(`Clothes`, `Colours`)
VALUES
('Jeans - Blue', NULL),
('Jumper - Red', NULL)
;
This the result of SELECT * FROM TableA; :
CLOTHES COLOURS
Jeans Blue
Jumper Red
I have a table with a column that has CSV.
TableA:
field_id | matches
---------------------
1 1,2,4,6,8,11,14,56
Now I need to get the field_id that matches a user given csv. So for instance, user string is 1,4,11, then it should return some value may be just true.
1.) Find_in_set does not work. Because it takes only one element and searches that in a SET/CSV column.
2.) Cannot use like concat('%,', user_input , ',%'). Because user input may not be in order.
Any other ideas? I guess this is a very common scenario.
Note: I dont need to search all records. I need to search a specific record. So in the above table, I just need to search one record that has field_id = 1. i.e. (where field_id = 1). (May not matter, but just an info)
Well, this is a good argument for having data in a proper relational form. But, you can try:
select t.*
from t
where (find_in_set($user_input, 1) = 0 or
find_in_set(substring_index(substring_index($user_input, ',', find_in_set($user_input, 1)), ',', -1), matches) > 0) and
(find_in_set($user_input, 2) = 0 or
find_in_set(substring_index(substring_index($user_input, ',', find_in_set($user_input, 2)), ',', -1), matches) > 0) and
. . .
Do this for however many values you might have in the userinput set.
I presume there is no straight solution with MySQL query like Find_In_Set. So I guess I will have to handle this with multiple queries or with Looping.
My table column has rows like this 1001, 1002, 1003 and so on separated by commas. There may be 1 or more comma separated values in each column. I need the total count of these comma separated values in the table. For example if my table has 2 rows one having 1001, 1002, 1003, 1004 and another with 1001, 1005 the output i should get is 6 i.e. 4+2. Kindly assist
there is no function in mysql to count char occurences. but you can replace every comma with nothing. and then you calculate the difference of lenghts which will give you the number of commas, which is one less than the number of values.
select
( LENGTH(col1) - LENGTH(REPLACE(col1, ',', '')) + 1 )
+ ( LENGTH(col2) - LENGTH(REPLACE(col2, ',', '')) + 1 )
AS valCount
from T;
(didn't test that explicitely but at least something very similar to that will do the job.)
replace()
length()
Try:
SELECT SUM(LEN(ColumnName) - LEN(REPLACE(ColumnName, ',', ''))) FROM TableName
This is one of those tasks that'd be much, much easier in the server-side script accessing your database than the database itself. Assuming you've already assigned the comma-separated strings to an array (where $array[1] is equal to the string from row 1:
$array = array("1001, 1002, 1003, 1004", "1001, 1005"); // assigned from database
foreach($array as $k => $v){
$numbersInString[$k] = count(explode(', ', $v));
}
echo implode(' + ',$numbersInString);
This is possible, with creative solutions (such as that from Raffael1984), in MySQL, but seems to much more easily, and concisely, implemented in PHP.
References:
count().
explode().
implode().
foreach().
Did you try using the count() function? You can specify which rows if you need to.
.row[COUNT(name)]
in your query. What does your table look like? I might be able to help more if I know what it looks like