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.
Related
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));
I have a table called "participants" that has 3 fields:
prt_id
prt_event_id
prt_participant_id
What I have is a select query with a where condition on event_id. The query returns let's say 20 rows (20 different participants). What I would like to do is to be able to figure out the row number for a given participant (prt_id).
SELECT *
FROM participants
WHERE prt_id = someinteger
While you can't specifically find a row ID using MySQL, you could do something like the following:
$conn = new mysqli(/*dbinfo*/);
$res = $conn->query("SELECT prt_id FROM participants");
$rowids = array(); $currid = 1;
while ($row = $res->fetch_object()) { // this is using the mysqli library
$rowids[$row->prt_id] = $currid;
$currid++;
}
This would give you an array of ids associated with prt_id.
You could do something like:
<?php
$counter = 1; // Start at one for first entry
$res = mysql_query("SELECT * FROM participants WHERE prt_id = 12");
while( $array = mysql_fetch_assoc($res) )
{
// Do something with the counter, store it into array with details
$counter++;
}
?>
This should do what you want inside MySQL (ie assign a rownum in the order of prt_id), but the performance will be dependent on the number of rows in the table so it's not optimal.
SELECT * FROM (
SELECT #tmp:=#tmp+1 rownum, p.*
FROM (SELECT #tmp:=0) z, participants p
ORDER BY prt_id
) participants
WHERE prt_id = 36;
Demo here.
Edit: This "doh level" rewrite uses an simple index range instead of a table scan, so should be much faster (provided prt_id is a PRIMARY KEY)
SELECT *, COUNT(p2.prt_id) ROWNUM
FROM participants p1
JOIN participants p2
ON p1.prt_id >= p2.prt_id
WHERE p1.prt_id=36;
Demo here.
you could just add an index column in your database, set it as int, primary key and auto increment. then when retrieving the row you retrieve the index number.
RowID is a feature of Oracle: http://docs.oracle.com/cd/B19306_01/server.102/b14200/pseudocolumns008.htm.
MySQL does not have something like that, you can basically emulate that by assign number to an array inside php as you retrieve each row, but that doesn't guarantee you the same number next time you retrieve that results. You probably have to settle for using one of the primary IDs
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.
i need to do this in php
lets say i have [1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,3,4,5,5,5,5,5,5,5,5,5,5,5,9,9,9,9,9]
i would like [1,5,9,2,3,4]
but my situation its a bit different, it's result from a mysql query
i have only an asociative atribute 'id_deseada', so i do now
while($row = mysql_fetch_array($result){
$t = $row['id_deseada'];
}
so instead of that, i guess i'd have to do domething like the first example; sort $result ordered by times of repetition and maybe beetter in a new array with a new field 'count' ?
This all begins because of this query:
SELECT articles.id as id_deseada,tags.* FROM articles,tags WHERE tags.id_article = articles.id AND tags.name IN ('name one','name two','form search inputs','...)
the problem is that returns one result for every tag and i want on result for every article..
First, generate the value array like this:
$vals = array();
while($row = mysql_fetch_array($result){
$vals[] = $row['id_deseada'];
}
Then, count and sort:
$valCounts = array_count_values($vals);
arsort($valCounts);
$result = array_keys($valCounts);
You can actually do it using an SQL query. For example, we have this table:
create table lol (id_deseada int);
insert into lol values (1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(2),(3),(4),(5),(5),(5),(5),(5),(5),(5),(5),(5),(5),(5),(9),(9),(9),(9),(9);
You can select the id_deseada's from the database sorting by repetitions by using grouping and ordering.
SELECT id_deseada FROM lol
GROUP BY id_deseada
ORDER BY COUNT(*) DESC
Result: 1, 5, 9, 2, 3, 4.
I have a table y Which has two columns a and b
Entries are:
a b
1 2
1 3
1 4
0 5
0 2
0 4
I want to get 2,3,4 if I search column a for 1, and 5,2,4 if I search column a.
So, if I search A for something that is in A, (1) I get those rows, and if there are no entries A for given value, give me the 'Defaults' (a = '0')
Here is how I would know how to do it:
$r = mysql_query('SELECT `b` FROM `y` WHERE `a` = \'1\';');
//This gives desired results, 3 rows
$r = mysql_query('SELECT `b` FROM `y` WHERE `a` = \'2\';');
//This does not give desired results yet.
//Get the number of rows, and then get the 'defaults'
if(mysql_num_rows($r) === 0) $r = mysql_query('SELECT `b` FROM `y` WHERE `a` = 0;');
So, now that it's sufficiently explained, how do I do that in one query, and what about performance concerns?
The most used portion would be the third query, because there would only be values in a for a number IF you stray from the defaults.
I think I have it:
SELECT b FROM y where a=if(#value IN (select a from y group by a),#value,0);
It checks if #value exists in the table, if not, then it uses 0 as a default.
#value can be a php value too.
Hope it helps :)
You can try something like this. I'm not 100% sure it will work because count() is a aggregate function but its worth a shot.
SELECT b
FROM table1
WHERE a = (
SELECT
CASE count(b)
WHEN 0 THEN :default_value
ELSE :passed_value
END
FROM table1
WHERE a = :passed_value
)
What about
$rows = $db->fetchAll('select a, b FROM y WHERE a IN (2, 0) ORDER BY a DESC');
if(count($rows) > 0) {
$a = $rows[0]['a'];
$i = 0;
while($rows[$i]['a'] === $a) {
echo $rows[$i++]['b']."\n";
}
}
One query, but overhead if there are a lot of 'zero' values.
Depends if you care about the overhead...
I think Michal Kralik best answer in my opinion based on server performance. Doing subselects or stored procedures for such simple logic really is not worth it.
The only way I would improve on Michal's logic is if you are doing this query multiple times in one script. In this case I would query for the 0's first, and then run each individual query, then checking if there was any value.
Pseudo-code
// get the value for hte zero's
$zeros = $db->fetchAll('select a, b FROM y WHERE a = 0');
//checking for 1's
$ones = $db->fetchAll('select a, b FROM y WHERE a = 1');
if(empty($ones)) $ones = $zeros;
//checking for 2's
$twos = $db->fetchAll('select a, b FROM y WHERE a = 2');
if(empty($twos)) $twos = $zeros;
//checking for 3's
$threes = $db->fetchAll('select a, b FROM y WHERE a = 3');
if(empty($threes)) $threes = $zeros;
You can do all this in a single stored procedure with a single parameter.
I have to run out, but I'll try to write one up for you and add it here as soon as I get back from my errand.
I don't know why this was marked down - please educate me. It is a valid, tested stored procedure, and I answered the question. The OP didn't require that the answer be in php. ??
Here's a stored proc to do what you want that works in SQL Server. I'm not sure about MySQL.
create proc GetRealElseGetDefault (#key as int)
as
begin
-- Use this default if the correct data is not found
declare #default int
select #default = 0
-- See if the desired data exists, and if so, get it.
-- Otherwise, get defaults.
if exists (select * from TableY where a = #key)
select b from TableY where a = #key
else
select b from TableY where a = #default
end -- GetRealElseGetDefault
You would run this (in sql server) with
GetRealElseGetDefault 1
Based on a quick google search, exists is fast in MySQL. It would be especially fast is column A is indexed. If your table is large enough for you to be worried about performance, it is probably large enough to index.