Let's say I have three values in PHP: "a", "b", "c". Doesn't matter whether in an array or comma separated string.
There is a table in database:
id | value
1 | a
2 | b
3 | d
My purpose is to find the values that are in php array but not in database table.
The given example will give "c".
Can I do it with only one query?
UPDATE
Received several good suggestions in answers about array_diff(), though in my case the DB table is really large and the array has not more than 5-6 items. So it would be better to perform 5-6 queries, I think.
If the PHP array is short, you can build a UNION ALL query to build your small table, then use NOT IN or LEFT JOIN query (whichever is faster) against the large table:
SELECT value
FROM (
SELECT 'a' AS value
UNION ALL
SELECT 'b'
UNION ALL
SELECT 'c'
) AS php_array_values
WHERE value NOT IN (
SELECT value
FROM that_large_table
);
Alternately, you can insert the php array values in a temporary table and use the IN or JOIN queries. Of course, this means you end up writing three extra queries:
CREATE TEMPORARY TABLE IF NOT EXISTS php_array_values (value VARCHAR(100));
DELETE FROM php_array_values;
INSERT INTO php_array_values VALUES ('a'), ('b'), ('c');
SELECT php_array_values.value
FROM php_array_values
LEFT JOIN that_large_table ON php_array_values.value = that_large_table.value
WHERE that_large_table.value IS NULL
how about this?
<?php
$a = array('a', 'b', 'c');
$values = implode("','", $a);
$sql = "SELECT DISTINCT `value` FROM `mytable` WHERE `value` IN ('$values')";
echo $sql;
perform the sql query. the result will be those 0 to 3 elements you already have. next, do an array_diff (which will not be heavy at all, since you'll have your initial small array, and the array of those in the db, which is even smaller).
$not_in_db = array_diff($a, $array_from_sql_result);
if what you have is a string with comma separated values, then you'll need to "explode" it first:
$s = "a,b,c";
$a = explode(",", $s);
You could select all the entries in the table and then perform an array_diff().
But this isn't one query, is one query and some post processing.
For this I would pull values from the table into an array and use array_diff
REF: http://www.php.net/manual/en/function.array-diff.php
Instead of pulling all the elements from the DB you can try using 'LIKE' statement, this will reduce the number of entries pulled from DB.
something like this :
PHP array values : a,b,c
DB values : a,b,d
select value from your_table_name where (value LIKE '%a%') OR (value LIKE '%b%') OR (value LIKE '%c%');
o/p of this will be : {a,b}
now use array_diff of php.
select max(*) as count from tablename
where field1 != $arr[0] && field2 != $arr[1] && field3 != $arr[2];
You can use and or or operators if you want. if the return count for this query is 0 then the array values are not exist in the database already.
Related
Ok, normally I would do something like this in PHP if I compared just few values:
// I want to get 1
$a = array(1,2);
$b = array(2);
// It produces 1
$result = array_diff($a, $b);
But in this case, $b is a large table in MySQL with millions of rows and retrieving it to PHP would be crazy, so my DBMS (MySQL) must do the work.
How could I do something like:
-- I need treating $a as a column
SELECT $a NOT IN (SELECT id FROM b);
Example
table B
|id|
2
3
4
array A
array(1, 2, 3, 5);
Result
A - B = A - INTERSECT(A,B) = [1, 5]
Example 2
This is exactly what I need for SQL server. Any ideas to do something similar in MySQL?
Note: A has about 5000 ids, so:
SELECT 1
UNION SELECT 1
UNION SELECT 1
UNION SELECT 2
UNION SELECT 5
...
...produces #1064 - memory exhausted
You can join the variables in the array $a in PHP and use that in your SQL query to retrieve the data, It would efficient in you case.
$in = join(',', array_fill(0, count($a), '?'));
$sql = "
SELECT *
FROM galleries
WHERE id IN ($in)"
Here is a working fiddle to prove that the SQL query will return the desired output.
EDIT :
According your new requirement, You could do the array comparison in
the PHP side because You have to somehow bring up the result from
MySQL to eliminate the result from PHP array. If you retrieve the
array $a from another table then we could have done something like a
join. But in this case we cannot do it. Also you mentioned that the
MySQL table has large number of rows so I assume PHP array will anyway
remove only couple of items from the results after bringing it to the
memory.
If you want to check the values exist or not, you can try this one
<?php
$some_id = "100";
$id = mysql_real_escape_string($some_id); // i use escape to compare IP ADDRESSES. you can remove it
$result = mysql_query("SELECT id FROM your_table WHERE id = '$id'");
if (!mysql_num_rows($result))
{
echo "Not Exists";
}
else
{
echo "Exists";
}
?>
I hope this will help
Consider using the JOIN NULL query:
strSQL = "SELECT t2.id
FROM tableB t1
RIGHT JOIN
(SELECT id
FROM tableB
WHERE id IN (".implode(',',$arrayA).")
) t2
ON t1.id = t2.id
WHERE t1.id IS NULL";
Also, depending on how large $arrayA is, consider appending to temp table and join above, replacing sub query with temp table.
This question already has answers here:
SELECT that returns list of values not occurring in any row
(6 answers)
Closed 6 years ago.
I have an array of a few values that I get from an uploaded file. I would like to identify which values in the file are not in my database.
I already know how to check the values one at a time by looping over the array and checking for matching records from the database for each value.
$stmt = $pdo->prepare('SELECT COUNT(*) FROM my_table WHERE a_column = ?');
foreach ($values as $value) {
$stmt->execute([$value]);
if ($stmt->fetchColumn() === '0') // do something with $value
}
But I was wondering if there is a way to get the results in one query. If I already had the values in an SQL table, it would be easy to find which ones had matching values in another table by joining the tables and checking the results for null values in the table in question.
SELECT i.value
FROM
imaginary_values_table i
LEFT JOIN my_table m ON i.value = m.a_column
WHERE
m.id IS NULL
Is it possible to do this type of query using a list of values instead?
I am familiar with IN, but I don't see how it could be used for this directly, because I am trying to find which items in an array are not in a table. With IN I could get all the rows from the table that are in my array, or with NOT IN I could get all the rows that are not in my array.
You can use a query like this:
SELECT t1.val
FROM (SELECT 1 AS val
UNION
SELECT 2 AS val
UNION
SELECT 3 AS val
...) AS t1
LEFT JOIN my_table AS t2 ON t1.val = t2.a_column
WHERE t2.a_column IS NULL
You can build the UNION subquery from your array of values:
$union = implode(' UNION ', array_map(function($val) {
return "SELECT $val AS val";
}, $array));
I found a different way to do this by selecting all the rows that match values in the array using IN and then using array_diff with the original array and the query result to get the ones that aren't there.
$qs = rtrim(str_repeat('?,', count($values)),',');
$stmt = $pdo->prepare("SELECT a_column FROM my_table WHERE a_column IN ($qs)");
$stmt->execute($values);
$not_there = array_diff($values, $stmt->fetchAll(PDO::FETCH_COLUMN));
People enter values into a form and on one entry of the form I have multiple values.
Eg:
One Entry for Name
and Multiple entries for hobbies.
I could enter into the db by running a for loop but then that would be multiple entries for the same name for each different hobby.
How can I enter one name and all hobbies with 'space' into one DB field 'TWIG'. I could use arrays but it shows up as ARRAY but then its back to FOR loop.
for ($t=0; $t<=$_POST['tc']-1; $t++) {
echo "<BR> ts:".$_POST[$t]."<BR>";
$ths[]=$_POST[$t];
}
print_r ($ths);
$statement = $link->prepare("INSERT INTO quest(cnos, dte, twig)
VALUES(:q, :d, :t )");
$statement->execute(array(
":q" => htmlspecialchars ($_POST['iht']),
":d" => $_SERVER['REQUEST_TIME'],
":t"=> $ths
));
One possibility is to implode your hobbies / concatinate your string into one...
for ($t=0; $t<=$_POST['tc']-1; $t++) {
$ths = $_POST[$t] . " "; //Concatinate string, do no use array!
}
//Cut off last character " " to avoid ugly space at the end:
$ths = substr($ths, 0, strlen($ths) - 1);
However a more clean solution is to make a more clear database structure if you want for atomic values.
This is an 1:n relation (Each of your entries in table A relates to n instances in table B).
Here is an example that can be adapted into your schema very easy:
Table User(id[PK], name, age);
Table User_Hobbies: (user_id, hobby_descr);
--
INSERT INTO User(2, "Chris", 19);
INSERT INTO USER_User_Hobbies(2, "videogames");
INSERT INTO USER_User_Hobbies(2, "music");
--
Example query:
SELECT u.name, u.age, GROUP_CONCAT(uh.hobby_descr) AS hobbies
FROM User u
LEFT JOIN User_Hobbies uh ON u.id = uh.user_id
WHERE u.id = 123 /*Where is optional, if you want to filter for a specific user*/
GROUP BY u.id;
Possible result:
| name | age | hobbies |
chris 18 videogames, music
steve 22 computer, programming, php
Use the implode function for this as follows:
":t"=> implode(" ", $ths);
I am not sure, if I understand your question right, but if you want to insert an array as a comma-seperated string (or separated by whatever), why don't use the php implode function: http://php.net/manual/de/function.implode.php
For example:
$myHobbies = array("football", "basketball", "running");
$statement = $link->prepare("INSERT INTO quest(cnos, dte, twig)
VALUES(:q, :d, :t )");
$statement->execute(array(
":q" => htmlspecialchars ($_POST['iht']),
":d" => $_SERVER['REQUEST_TIME'],
":t"=> implode(" ", $myHobbies)
));
I think to use comma's or semicolons as separator, is better than whitespaces, since some of the hobbies could consists of two words (for example "pc gaming").
I think that better solution is to use json_encode and not implode, since it provides more robust structure.
":t" => json_encode($myHobbies)
You can use join function to store multiple value in same field in database may be it will works:-
":t"=>join(",", $ths);//if you want ,(coma) between two string
or
":t"=>join(" ", $ths);//if you want space between two string
It seems that I have to edit my question again - these down votes are getting frustrating...
I have an array of ids and a table to check it with. This is the query I use:
SELECT COUNT(*) FROM orders WHERE order_id IN (...
When this number isn't equal to the number of ids in the array, I need to get those values from the array that I have, that do not exist in the table. Is there a more optimal way to do this than making some temporary table, and selecting from it?
EDIT: For all those suggesting inserting NOT in the first query - I do not need all the rows from my table that do not match the values in the array - I need the values from the ARRAY itself...
For example, I have an array of three ids - "1","2","3". In the table I have ids "2" and "3". I check the array with my query:
SELECT COUNT(*) FROM orders WHERE order_id IN (1,2,3)
I get the number 2 as a result of this query, and that doesn't match the number of ids I have in the array, which is 3. I need a query that will get the missing id from the array I submitted, in this case the id I need is "1"
I don't think there's a clean SQL-only solution, because there's no way to SELECT from an array. However, instead of using a temporary table, you could return all the IDs that are found in the table, and do the NOT on the PHP side.
So select the result of this into an array, $existing_orders:
SELECT order_id FROM orders WHERE order_id IN (...);
Then you have:
$all_orders = array(1, 2, 3); // IDs you're looking for
$existing_orders = array(2, 3); // IDs that were found in the table
$not_existing_orders = array_diff($all_orders, $existing_orders);
print_r($not_existing_orders); // Array( [0] => 1 )
OK THAT´S clear now:
Then you will have to make something like this:
select id from order where id IN ( your list )
create a second array with the found values
and then use php array function array_diff
select * from orders where order_id NOT in (...
I have 31 separate tables (actually I have 365, but lets keep this simple) in a MySQL database, each containing data for a given day. The tables are (badly) named based on the day.
Example:
island01Aug07
island02Aug07
island03Aug07
island04Aug07
...
island31Aug07
I would like to combine all the tables into one master table:
island_08
It would be simple to use INSERT INTO but my problem is that the tables do not have a column to denote the day. It would have to be added into the destination table, and then I would need to populate that when moving/copying the tables over.
Suggestions, advice and solutions welcome.
CREATE TABLE island_08 (mydate DATE NOT NULL, field1 …)
INSERT
INTO island_08 (mydate, field1, field2)
SELECT '2007-07-01', field1, field2
FROM island01Aug07
UNION ALL
SELECT '2007-07-02', field1, field2
FROM island02Aug07
UNION ALL
…
As alternative option you can list all tables in to array like table_name=>mysql_date,
after that loop through and copy data from one table and insert in to another. After data was transferred successfully you can remove the table.
Here is example of getting list of tables and extracting date from it:
$prefix = 'island';
$lenght = strlen($prefix);
$result = $this->query("SHOW TABLES LIKE '{$prefix}%'");
$arrayDates = array();
if($db->num_rows($result))
{
while($v = $db->fetch_array($result))
{
$mysql_table = current($v);
$arrayDates[$mysql_table] = date('d-m-Y',strtotime(substr($mysql_table,0,$lenght)));
}
}
//Now you can walk through your array and copy data from one table tyo another and append you mysql value