fetch the comma separate field in mysql query - php

My table id and skillid values are
1=>3
2 =>5
3 =>5,6,8
I have tried the query to fetch the value from my table
SELECT * FROM mytable WHERE skillid IN ('3', '5');
But i am getting only two rows, that was first and second row.
But i need the result three rows.
Thanks in advance.

The 'IN' comparison operator works like an equality operator. This:
expr IN ('3','5')
is equivalent to:
( expr = '3' OR expr = '5' )
That should be sufficient to explain why your query is not returning what you expect.
If your table has a character column that contains a comma separated list, like this:
id skill_id_list
-- -------------
1 3
2 5
3 5,6,8
Then one option, to search for a particular id value in the list is to make use of the MySQL FIND_IN_SET function. For example:
SELECT t.id
, t.skill_id_list
FROM mytable t
WHERE FIND_IN_SET('3', t.skill_id_list)
OR FIND_IN_SET('5', t.skill_id_list)
The MySQL FIND_IN_SET function is documented here:
https://dev.mysql.com/doc/refman/5.5/en/string-functions.html#function_find-in-set
Of course, that's only one option. There are several other ways to get an equivalent result.
If there are no spaces in the skill_id_list column, then another option is to use a LIKE operator, but taking care to handle the edge cases.
There are four cases you would need to check for the id value appearing:
as the only one in the list
at the beginning of the list
in the middle of the list
at the end of the list
To explicitly handle those four cases, you could check each one, for example:
WHERE skill_id_list LIKE '3' -- only one in list
OR skill_id_list LIKE '3,%' -- beginning of list
OR skill_id_list LIKE '%,3,%' -- middle of list
OR skill_id_list LIKE '%,3' -- end of list
Or, another way to approach that is to turn all of those cases into the single "middle of the list" by just appending a comma to the beginning and end of the list, and then doing a single check, for example:
WHERE CONCAT(',',skill_id_list,',') LIKE '%,3,%'
Note that spaces embedded in the list could cause a row not to match. The MySQL REPLACE function can be used to remove spaces, replacing all spaces with an empty string, something like this:
WHERE CONCAT(',',REPLACE(skill_id_list,' ',''),',') LIKE '%,3,%'
NOTE
The preceding attempts to to answer the question you asked. The following addresses a fundamentally different (though closely related) issue, concerning the relational dataa model and normalization.
Repeating attributes in normalized relational model are represented in separate table. That's the normative relational model. We typically avoid storing comma separated lists, and instead implement a separate table to store the repeating attributes.
For example, if a job has multiple skills, we would typically create another table to hold the list of job skills:
CREATE TABLE job
( id INT NOT NULL PRIMARY KEY
, description VARCHAR(10)
);
CREATE TABLE job_skill
( job_id INT NOT NULL COMMENT 'FK ref job.id'
, skill_id INT NOT NULL COMMENT ''
, PRIMARY KEY (job_id, skill_id)
, FOREIGN KEY job_skill_FK1 (job_id) REFERENCES job (id)
);
We'd represent the data in your model as five separate rows:
job_skill
job_id skill_id
------ --------
1 3
2 5
3 5
3 6
3 8
That's the normative pattern.
To get back a list of job ids that require skills 3 or 5:
SELECT s.job_id
FROM job_skill s
WHERE s.skill_id in (3,5)
GROUP BY j.job_id
To get back a list of job ids that require skills 3 and 5, there's several ways to do that, for example:
SELECT s.job_id
FROM job_skill s
ON s.job_id = t
WHERE s.skill_id in (3,5)
GROUP BY j.job_id
HAVING COUNT(DISTINCT s.skill_id) = 2
If it's more convenient for your use case to get back a comma separated list as a string, you can use the GROUP_CONCAT aggregate function:
SELECT j.id AS job_id
, GROUP_CONCAT(s.skill_id ORDER BY s.skill_id) AS skill_id_list
FROM job j
LEFT
JOIN job_skill s
ON s.job_id = j.id
WHERE ...
ORDER BY ...

Please check
SELECT * FROM mytable WHERE 5 IN (skillid) OR 3 IN (skillid);

SELECT * FROM mytable WHERE skillid LIKE '3%' OR skillid LIKE '5%';
Definetly this is NOT the way to do it...
YOU SHOULD NORMALIZE your table to avoid this kind of problems

This is not good practice, you should normalize your table table, but REGEXP would be helpful
Please try this way
SELECT * FROM mytable WHERE
skillid REGEXP '[[:<:]]3[[:>:]]' OR skillid REGEXP '[[:<:]]5[[:>:]]'

Related

Given a row name (m) find column names (n) when (m,n)=x in a MySQL database as part of a PHP query

I am building a website as a diagnostic aid for neurological conditions. It is coded in html and communicates with a MySQL database via PHP. The primary table which feeds information to the website is structured as follows:
Image showing table structure with rows representing Neurological Conditions and columns providing information on symptoms associated with these conditions
The table above can be reproduced using the following MySQL code:
CREATE TABLE IF NOT EXISTS my_table (
`Condition` VARCHAR(22) CHARACTER SET utf8,
`Diarrhoea` INT,
`Headache` INT,
`Hyporeflexia` INT,
`Hypoaesthesia_Spinothalamic` INT
);
INSERT INTO my_table VALUES
('Abetalipoproteinaemia',1,NULL,1,NULL),
('Caffeine toxicity',1,1,NULL,NULL),
('Vitamin B12 deficiency',NULL,NULL,1,2);
SELECT * FROM my_table;
Cell values are as follows:
(m,n)=1 if condition and symptom are associated
(m,n)=2 if condition and symptom CANNOT be associated. The presence of this symptom excludes the condition as a possible diagnosis.
(m,n)=null if no information exists or if symptom and condition are not associated
I'm struggling to write an SQL query which will identify all the columns (n) for a specific condition (m) where the value of the cell (m,n) = 2.
So far my reading has highlighted ideas about pivot tables (I can't see how I would be able to use them for this problem) and database normalisation which I don't think is possible because of the other queries I am running on the same table.
An example based on the table above:
Patient presents with hyporeflexia
SQL query identifies this could be cause by either "abetalipoproteinaemia" or "vitamin B12 deficiency" - this all works fine already
I want to establish whether any of the conditions identified (abetalipoproteinaemia and vitamin B12 deficiency) have symptoms that would exclude the diagnosis (any cell in that row = 2) and return the name of any column (symptom) for which this is the case.
A query to the SQL database identifies vitamin B12 deficiency would be excluded as a possible diagnosis if spinothalamic hypoesthesia is present - this will be fed back to the html display.
Any help would be much appreciated - thanks for your time!
I think it would be more usual to arrange the data something like this - apologies for any spelling errors or poor terminology, but if you pay peanuts...
syptom condition exclusion
Abetalipoproteinaemia Diarrhoea 0
Abetalipoproteinaemia Hyporeflexia 0
Caffeine toxicity Diarrhoea 0
Caffeine toxicity Headache 0
Vitamin B12 deficiency Hyporeflexia 0
Vitamin B12 deficiency Hypoaesthesia Spinthalamic 1
You would then take this one or two steps further, and have a table for symptoms, a table for conditions, and a table which says which symptom relates to which condition, and how.
Query pattern would be much more straightforward if the table were designed following normative relational patterns.
Consider the resultset returned by a query of this form:
SELECT v.condition
, v.symptom
, v.associated_or_excluded
FROM ( SELECT t1.`Condition` AS `condition`
, 'Diarrhoea' AS `symptom`
, t1.`Diarrhoea` AS `associated_or_excluded`
FROM mytable t1
UNION ALL
SELECT t2.`Condition`
, 'Headache'
, t2.`Headache`
FROM mytable t2
UNION ALL
SELECT t3.`Condition`
, 'Hyporeflexia'
, t3.`Hyporeflexia`
FROM mytable t3
UNION ALL
SELECT t4.`Condition`
, 'Hypoaesthesia_Spinothalamic'
, t4.`Hypoaesthesia_Spinothalamic`
FROM mytable t4
) v
We could use that query as an inline view (a rowsource) for an outer query, or a new table could be populated with the result from this query INSERT ... SELECT to convert.
With that resultset, with the data in standard relational form, we avoid the struggle by writing a simple query like this:
SELECT t.symptom
FROM ( ... ) t
WHERE t.condition = 'Hyporeflexia'
AND t.associated_or_excluded = 2
that will return symptoms that are excluded from a particular condition.
(or, to put it in terms of the original question, where a value of 2 is found the intersection of m and n)
Note that ( ... ) is replaced with a table name or with an inline view returning the result from query above.
Note that the entirety of the "struggle" is inside the parens, with the inline view query that gets the data represented in a suitable form.
SELECT t.symptom
FROM ( -- inline view query
SELECT t1.`Condition` AS `condition`
, 'Diarrhoea' AS `symptom`
, t1.`Diarrhoea` AS `associated_or_excluded`
FROM mytable t1
UNION ALL
SELECT t2.`Condition`
, 'Headache'
, t2.`Headache`
FROM mytable t2
UNION ALL
SELECT t3.`Condition`
, 'Hyporeflexia'
, t3.`Hyporeflexia`
FROM mytable t3
UNION ALL
SELECT t4.`Condition`
, 'Hypoaesthesia_Spinothalamic'
, t4.`Hypoaesthesia_Spinothalamic`
FROM mytable t4
) t
WHERE t.condition = 'Hyporeflexia'
AND t.associated_or_excluded = 2

Checking for value in pdo query from string [duplicate]

I need help for this problem.
In MYSQL Table i have a field :
Field : artist_list
Values : 1,5,3,401
I need to find all records for artist uid 401
I do this
SELECT uid FROM tbl WHERE artist_list IN ('401');
I have all record where artist_list fields values are '401' only, but if i have 11,401 this query do not match.
Any idea ?
(I cant user LIKE method because if artist uid is 3 (match for 30, 33, 3333)...
Short Term Solution
Use the FIND_IN_SET function:
SELECT uid
FROM tbl
WHERE FIND_IN_SET('401', artist_list) > 0
Long Term Solution
Normalize your data - this appears to be a many-to-many relationship already involving two tables. The comma separated list needs to be turned into a table of it's own:
ARTIST_LIST
artist_id (primary key, foreign key to ARTIST)
uid (primary key, foreign key to TBL)
Your database organization is a problem; you need to normalize it. Rather than having one row with a comma-separated list of values, you should do one value per row:
uid artist
1 401
1 11
1 5
2 5
2 4
2 2
Then you can query:
SELECT uid
FROM table
WHERE artist = 401
You should also look into database normalization because what you have is just going to cause more and more problems in the future.
SELECT uid
FROM tbl
WHERE CONCAT(',', artist_list, ',') LIKE '%,401,%'
Although it would make more sense to normalise your data properly in the first place. Then your query would become trivial and have much better performance.

how ignore duplicate values without consider of their position in mysql?

I have this table with one column
A:
16654,16661
16661,16654
16670,16717
16717,16670
I want to have this: (ignore duplicate values without consider of their position)
16661,16654
16670,16717
is there any math function that operate between two number and have unique result?
actually i have this table ( name:class)
id second_code have_second_code
1 0 no
2 3 yes
3 2 yes
4 5 yes
5 4 yes
when "have_second_code" is "yes"
column second_code have a value!
id is primary
second code is from id column and there is a binary relation between them. now i need this output 2,3 and 4,5
SELECT rowone, rowtwo, rowonemillion FROM yourtable GROUP BY(nodupecolumn)
I suppose, that your query that produces this one-column-multiple-values-table uses GROUP_CONCAT(). In this case you need to do it like this:
SELECT DISTINCT GROUP_CONCAT(DISTINCT whatever_column ORDER BY whatever_column) FROM ...
Use the DISTINCT keyword two times. In GROUP_CONCAT(), so that duplicates are removed from the comma separated values, and one time outside of GROUP_CONCAT(), so that duplicate rows are removed. The ORDER BY in GROUP_CONCAT() is important, otherwise the outer DISTINCT won't detect duplicates. Also note, that (outer) DISTINCT works on the whole row, not just one column.

Mysql with regular expression

I have a query regarding regular expression.I have design a table which contain three column one column contain member ids which are separated by commas.I am showing you my table structure.Please follow
send_id member_id
1 1211,23,34
2 1,23
I want to select only send_id 2 data which contain member_id as 1.
this is the query that i am using
SELECT * FROM table WHERE column REGEXP '^[1]+$';
but this query giving me both row.Please help me.
With Regards
Rahul
Never store separate values in one column
Normalize your structure like
send_id member_id
1 1211
1 23
1 34
2 1
2 23
If you still want your regex, then it will be
SELECT * FROM t WHERE column REGEXP '(^|[^0-9])1([^0-9]|$)'
First, you should be normalizing your data so you're not in this horrible mess in the first place. Here's a good resource explaining normalization.
Second, I believe your problem lies with your regular expression. Try this instead:
SELECT * FROM table WHERE column REGEXP '^[1]$';
The regular expression you're using uses the [1]+ group. The + means it has to match [1] 1 or more times, hence why you're getting two rows instead of one. Removing the + means it will match [1] once.
However, that still won't fix your problem, as more than one row contains 1. This is why normalization is so important.
Having multiple values inside a column isn't a good practice for designing a DB.
You should normalize your data, i.e., put just one piece of atomic information inside each element of your table.
You can find more information regarding to this in Wikipedia:
http://en.wikipedia.org/wiki/Database_normalization
Like they have told you, perfect solution would be normalize your data, I think Alma Do Mundo answer explains it quite well.
If you want to use REGEXP anyway you have to take in account four approaches; id is the only one, id is the first, id is in the middle and id is at the end. I have use id=74 for the example:
SELECT * FROM table WHERE member_id REGEXP '(^74$|^74,|,74,|,74$)';
depending on your requirements, you should either normalize your data i.e. make 3 tables, one with the send ID, one with the member id, and one that combines the two, then you can link them up with INNER JOINS.
However, if you are going to do it that way, you can use a "WHERE member_id LIKE %1%" to pull in all the relevant fields. You'll have to use the application to filter the relevant records.
In any case, if you're not going to normalize the data you will have to use the front end to filter out the results.
An example of the inner join syntax would look like this
SELECT * FROM SendTable
JOIN Send_Member ON SendTable.send_id = Send_Member.send_id
JOIN Member ON Member.member_id = Send_Member.member_id
WHERE Member.member_id = 1;
where the schema looks like:
Sendtable:
send_Id (primary key)
...other fields
Send_Member:
send_id (primary key and foreign key to SendTable)
member_id (primary key and foreign key to member)
...any fields you might want that are relevant to the particular send table and member table link
Member:
member_id (primarykey)
...other fields

Comma separated values in MySQL "IN" clause

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

Categories