Say if I have an array and I want to check if an element is a part of that array, I can go ahead and use in_array( needle, haystack ) to determine the results. I am trying to see the PHP equivalent of this for my purpose. Now you might have an instant answer for me and you might be tempted to say "Use IN". Yes, I can use IN, but that's not fetching the desired results. Let me explain with an example:
I have a column called "pets" in DB table. For a record, it has a value: Cat, dog, Camel
(Yes, the column data is a comma separated value). Consider that this row has an id of 1.
Now I have a form where I can enter the value in the form input and use that value check against the value in the DB. So say I enter the following comma separated value in the form input: CAT, camel
(yes, CAT is uppercase & intentional as some users tend to enter it that way).
Now when I enter the above info in the form input and submit, I can collect the POST'ed info and use the following query:
$search = $_POST['pets'];
$sql = "SELECT id FROM table WHERE pets IN ('$search') ";
The above query is not fetching me the row that already exists in the DB (remember the record which has Cat, dog, Camel as the value for the pets column?). I am trying to get the records to act as a superset and the values from the form input as subsets. So in this case I am expecting the id value to show up as the values exist in the column, but this is not happending.
Now say if I enter just CAT as the form input and perform the search, it should show me the ID 1 row.
Now say if I enter just camel, cAT as the form input and perform the search, it should show me the ID 1 row.
How can I achieve the above?
Thank you.
The function you're looking for is find_in_set:
select * from ... where find_in_set($word, pets)
for multi-word queries you'll need to test each word and AND (or OR) the tests:
where find_in_set($word1, pets) AND find_in_set($word2, pets) etc
IN() Check whether a value is within a set of values
mysql> SELECT 2 IN (0,3,5,7);
-> 0
mysql> SELECT 'wefwf' IN ('wee','wefwf','weg');
-> 1
SELECT val1 FROM tbl1 WHERE val1 IN (1,2,'a');
View: IN MySql
I've got several things for you in terms of feedback & in direct response to your questions:
First, I suggest you sanitize the input. Everybody is going to tell you that. For that, see What’s the best method for sanitizing user input with PHP?.
Second, normalize the input with UPPER() or LOWER() if you want to use MySQL and need to store user-formatted input, or use strtoupper() and strtolower() if you wanted to process the input before storing it.
You're still left with the order in the user query. E.g. "cat, dog" ought to yield the same result as "dog, cat". If you were to code that with a LIKE statement, performance issues are going to eat you alive. Not only would you have to create the query dynamically, you'd also end up with huge and unnecessarily complex queries. In short, forget it. You have to change the way you store your data.
One way to accomplish this is by creating a relationship table that references a table of unique user input and your record. This table would look similar to
user_id | pet_id
Every user could have more than one pet_id associated with them. I've set up a database a long time ago the same way you did and ran into the same issues. Performance-wise it never paid off and it's anything but good style. I ended up changing my structure because of that to the above-mentioned method.
This mysql function search an INT value into a json array of INT:
-- EXAMPLES :
-- select is_into_json_array(18, '[25, 10, 15]'); -> -100
-- select is_into_json_array(25, '[25, 10, 15]'); -> 0
-- select is_into_json_array(15, '[25, 10, 15]'); -> 2
https://github.com/PietroLaGrotta/Json-in-mysql-text-type/blob/master/is_into_json_in_array.sql
Yes, the column data is a comma
separated value
Here is your fault.
No, it shouldn't be comma separated value
And your database structure should be normalized.
Related
I'm a little bit stuck.
I have an SQL column that contains weather codes (like Rain, Snow, etc.)
It is comma separated so the column would have a value of something like
rain,snow,haze
Now, I want to select the rows that contain values from an array.
I have an SQL code that is something like this:
SELECT * FROM locations WHERE currentWeather IN ('rain', 'snow', 'cloudy') ORDER BY name ASC
The problem is that this obviously works when currentWeather column only contains one item.
Is there a way to do it so that if the column value contains any of the items from the given array, it selects it?
Also, would it select it twice if two items match?
Best wishes
Use unnest in a subselect.
Select distinct A.myArray from (select unnest(column) as myArray from table) A where A.myArray in (your words to filter for)
Notice that using arrays in sql isn't very ideal and does not follows normalization rules. Your tables should ideally not contain arrays but rather just several rows each one containing the specific value you Want. It prevents issues such as this one.
To avoid the selection of repeated values, use the Distinct keyword right after you write select.
Rsference:
https://www.w3resource.com/PostgreSQL/postgresql_unnest-function.php
WHERE FIND_IN_SET(currentWeather, "rain,snow,cloudy")
Picks apart the string at commas (only) to see if currentWeather is any one of those 3 'words'.
See also FIELD(...)
I have 3 tables :
-User
-SuperUser
-SpecialUser
SpecialUser and SuperUser are extensions of the User table. Let's say that User table has {name, email}, SuperUser has {idUser, superPower} and specialUser has {idUser, hairColor}.
Of course a SpecialUser and a SuperUser have a name and email from the user table. This means that a User can be a SuperUser or a SpecialUser.
My question is how do I perform a query that gets all the info of a user (I don't know before the query if he is a specialUser or SuperUser).
I thought about 2 methods :
-Putting a column "userType" (0 : he is specialUser, 1 : he is superUser) in the user table
With this method, should I do a MySQL query with IF inside ? (What would be the query). Or should I do simple query (getting the user table alone) then in PHP I do a if and query the right table (super or special table)
OR
-Not putting any column and doing a MySQL query with 2 joins on the id of the user (technically one of the 2 joins won't return anything)
Which one should be used ? (I care about speed performance, less about memory - Let's say that the tables have over 1 million rows)
How I would do this is in pseudo-sql:
Select * from user
left outer join superuser
left outer join specialuser
And return everything. If superuser's fields are not null, then in the PHP you can operate on that, same for specialuser. And this gives you two advantages:
1) You don't need a field to say what kind of user the user is anymore, the contents of the join will tell you.
2) You can have a user be a superuser and a specialuser at once, if you wished. (And if you didn't want that to happen, you can prevent it using a constraint or similar)
To do this without branching might be tricky, though I have an idea; since you said memory is not a problem,
but performance is.
First off make a column in the User table and call it userType like you suggested and store the contents of the table name followed by the table columns in a parse-able string like:
(SuperUser/SpecialUser), ("idUser, superPower"/"idUser, hairColor")
Example:
"SuperUser, idUser, superPower" //for a SuperUser
In psuedo code:
SELECT `userType` FROM `usersTable` WHERE name = [put var here]
Let the returned value be valRet
parse the valRet into an array... //the first value is the table name, the remaining values are column names
make a second sql query based on the array
Performance wise I believe this is good because there is no branching. However, I'm not sure what kind of performance hit you would take on the string parsing. Try benchmarking it on a few thousand queries to see.
//Also check out the answer to a similar question here:
Using an IF Statement in a MySQL SELECT query
i need to sort through a column in my database, this column is my category structure the data thats in the column is city names but not all the names are the same for each city, what i need to do is go through the values in the column i may have 20-40 value that are the same city but written differently i need a script that can interpret them and change them to a single value
so i may have two values in the city column say:( england > london ) and ( westlondon ) but i need to change to just london, is there a script out there that is capable of interpreting the values that are already there and change them to the value would want i know the dificult way of doing this one by one but wondered if there was a script in any language that could complete this
I've done this sort of data clean-up plenty of times and I'm afraid I don't know of anything easier than just writing your own fixes.
One thing I can recommend is making the process repeatable. Have a replacement table with something like (rulenum, pattern, new_value). Then, work on a copy of the relevant bits of your table so you can just re-run the whole script.
Then, you can start with the obvious matches (just see what looks plausible) and move to more obscure ones. Eventually you'll have 50 without matches and you can just manually patch entries for this.
Making it repeatable is important because you'll be bound to find mis-matches in your first few attempts.
So, something like (syntax untested):
CREATE TABLE matches (rule_num int PRIMARY KEY, pattern text, new_value text)
CREATE TABLE cityfix AS
SELECT id, city AS old_city, '' AS new_city, 0 AS match_num FROM locations;
UPDATE c SET c.new_city = m.new_value, c.match_num = m.rule_num
FROM cityfix AS c JOIN matches m ON c.old_city LIKE m.pattern
WHERE c.match_num = 0;
-- Review results, add new patterns to rule_num, repeat UPDATE
-- If you need to you can drop table cityfix and repeat it.
Just an idea: 16K is not so much. first use Perl's DBI (im assuming you are going to use Perl) to fetch that city column, store it in a hash (city name as the hash), then find your an algorithm that suites your needs (performance wise) to iterate over the hash keys and use String::Diff to find matching intersection (read about it, it definitely can help you out) and store it as a value.. then you can use that to update the database using the key (old value) and the value as the new value to update.
Imagine it like this. There is a field in my database called flags in which are added or removed data like this:
UPDATE people SET flags=flags|16 WHERE ....
UPDATE people SET flags=flags|128 WHERE ....
UPDATE people SET flags=flags&~16 WHERE ....
UPDATE people SET flags=flags&~128 WHERE ....
For instance this field can have value like 65536 or more or less. My question is - How to get specific flag from this field using PHP code? I mean something like this:
SELECT * FROM people WHERE flags=16;
But the result will return all people with not just number 16 in field but it will return people with flag 65536, people with 16 but not people with 2 or 1. Which SELECT query should I use here in my php code or maybe some specific PHP integrated functions? Thank you.
Assuming flags is a bitfield and you want to select rows where bit #4 (10000) is set
SELECT * FROM people WHERE flags & 16;
This is not ideal though as you're losing out on all that referential goodness that DBs are good for.
What you should have is two new tables; flags and people_flags. The former contains all the flags (id and name columns should be sufficient). The latter contains flag_id and people_id columns, creating a many-to-many relationship (see junction table).
The logic is not correct though. You cant have just one flag that contains 16 as its not unique.
Lets say you have DB with this rows
name:Jhon tel:012345 email:test#test.com flags:16
name:Mike tel:012344 email:mike#test.com flags:16
name:Sarah tel:012346 email:sarah#test.com flags:2442
to select flags 16 from this table with using Select statement you will get two rows of data you can either use Distinct or get flags to be unique
I'm trying to figure out how and which is best for storing and getting multiple entries into and from a database. Either using explode, split, or preg_split. What I need to achieve is a user using a text field in a form to either send multiple messages to different users or sharing data with multiple users by enter their IDs like "101,102,103" and the PHP code to be smart enough to grab each ID by picking them each after the ",". I know this is asking a lot, but I need help from people more skilled in this area. I need to know how to make the PHP code grab IDs and be able to use functions with them. Like grabbing "101,102,103" from a database cell and grabbing different stored information in the database using the IDs grabbed from that one string.
How can I achieve this? Example will be very helpful.
Thanks
If I understand your question correctly, if you're dealing with comma delimited strings of ID numbers, it would probably be simplest to keep them in this format. The reason is because you could use it in your SQL statement when querying the database.
I'm assuming that you want to run a SELECT query to grab the users whose IDs have been entered, correct? You'd want to use a SELECT ... WHERE IN ... type of statement, like this:
// Get the ids the user submitted
$ids = $_POST['ids'];
// perform some sanitizing of $ids here to make sure
// you're not vulnerable to an SQL injection
$sql = "SELECT * FROM users WHERE ID IN ($ids)";
// execute your SQL statement
Alternatively, you could use explode to create an array of each individual ID, and then loop through so you could do some checking on each value to make sure it's correct, before using implode to concatenate them back together into a string that you can use in your SELECT ... WHERE IN ... statement.
Edit: Sorry, forgot to add: in terms of storing the list of user ids in the database, you could consider either storing the comma delimited list as a string against a message id, but that has drawbacks (difficult to do JOINS on other tables if you needed to). Alternatively, the better option would be to create a lookup type table, which basically consists of two columns: messageid, userid. You could then store each individual userid against the messageid e.g.
messageid | userid
1 | 1
1 | 3
1 | 5
The benefit of this approach is that you can then use this table to join other tables (maybe you have a separate message table that stores details of the message itself).
Under this method, you'd create a new entry in the message table, get the id back, then explode the userids string into its separate parts, and finally create your INSERT statement to insert the data using the individual ids and the message id. You'd need to work out other mechanisms to handle any editing of the list of userids for a message, and deletion as well.
Hope that made sense!
Well, considering the three functions you suggested :
explode() will work fine if you have a simple pattern that's always the same.
For instance, always ', ', but never ','
split() uses POSIX regex -- which are deprecated -- and should not be used anymore.
preg_split() uses a regex as pattern ; and, so, will accept more situations than explode().
Then : do not store several values in a single database column : it'll be impossible to do any kind of useful work with that !
Create a different table to store those data, with a single value per row -- having several rows corresponding to one line in the first table.
I think your problem is more with SQL than with PHP.
Technically you could store ids into a single MySQL field, in a 'set' field and query against it by using IN or FIND_IN_SET in your conditions. The lookups are actually super fast, but this is not considered best practice and creates a de-normalized database.
What is nest practice, and normalized, is to create separate relationship tables. So, using your example of messages, you would probably have a 'users' table, a 'messages' table, and a 'users_messages' table for relating messages between users. The 'messages' table would contain the message information and maybe a 'user_id' field for the original sender (since there can only be one), and the 'users_messages' table would simply contain a 'user_id' and 'message_id' field, containing rows linking messages to the various users they belong to. Then you just need to use JOIN queries to retrieve the data, so if you were retrieving a user's inbox, a query would look something like this:
SELECT
messages.*
FROM
messages
LEFT JOIN users_messages ON users_messages.message_id = messages.message_id
WHERE
users_messages.user_id = '(some user id)'