MySQL using AND in a single column [duplicate] - php

This question already has answers here:
How to return rows that have the same column values in MySql
(3 answers)
Closed 5 years ago.
I'm working on a tag system where an array (of tags) is queried and all rows with the same tags in the Tag column are selected.
The issue is that I used the IN condition which is a type of an OR function, which selected all the rows with the same tags as opposed to narrowing them down, for example.
Instead of narrowing down an image with tags like 'sun' and 'landscape' it would select all images with those tags.
What I'm looking for is an AND version of IN () or a substitute that can work with arrays.
This is just an example. In reality, the user can add as many tags as they want
+----+---------+---------+
| ID | ImageID | Tag |
+----+---------+---------+
| 1 | 2 | sun |
+----+---------+---------+
| 2 | 12 |landscape|
+----+---------+---------+
| 3 | 15 | field |
+----+---------+---------+
| 4 | 15 |landscape|
+----+---------+---------+
My code
$tag = $_POST['tag'];
$tag = preg_split("#/#", $tag);
$tag = implode("', '", $tag);
$query = "SELECT * FROM ComicStripTags WHERE `Tag` IN ('$tag')";
$result = mysqli_query($link, $query);
while ($row = mysqli_fetch_array($result)){
$ID[] = $row['ImageID'];
}
Thanks
P.S. I'm not working in SQL, im working in PHP

You can do it using inner joins:
select * from TAGS a
inner join TAGS b
on a.ImageID = b.ImageID
where a.tag = 'field'
and b.tag = 'landscape'
According to your PHP code:
$joins = [];
$conditions = [];
foreach (preg_split("#/#", $_POST['tag']) as $index => $tag) {
$alias = "ComicStripTags_$index";
$joins[] = "ComicStripTags AS $alias" . ($index > 0 ? " ON ComicStripTags_0.ImageID = $alias.ImageID" : '');
$conditions[] = "$alias.Tag = '$tag'";
}
$query = sprintf("SELECT * FROM %s WHERE %s", implode(' INNER JOIN ', $joins), implode(' AND ', $conditions));

Assuming each ImageID can have only one of each tag, you can group by ImageID and select only rows where the count of tags equals the size of the input array.
SELECT ImageID FROM ComicStripTags
WHERE Tag IN ('landscape', 'field')
GROUP BY ImageID HAVING COUNT(Tag) = 2;

Alternatively you can do some ranking which would support partial tag matches as well:
SELECT
ImageID,
group_concat(Tag) as 'matched_tags',
count(*) as 'tag_matches'
FROM TAGS
WHERE Tag IN ( [list_of_tags] )
GROUP BY ImageID
HAVING tag_matches >= [minimum_number_of_tags]
ORDER BY tag_matches DESC

Related

mysql - multiple select with UNION ALL and count() in the same query

I have a DB-table formed like this:
ID | pairname | timeframe | value1 | value2 | value3
I have about 290 pairnames and 4 timeframes and for each timeframe I will have 250 rows.
Table looks like this:
1 | appusd | 1h | 12319 | 129312 | 11111 | 11111
2 | appusd | 1h | 12444 | 123912 | 11221 | 111231
3 | appusd | 4h | 12312 | 123123 | 11111 | 12321
4 | gttusd | 1h | 12342 | 123123 | 11111 | 12321
5 | gttusd | 4h | 12342 | 123123 | 11111 | 12321
I run a PHP script, that will first fill the DB with values and then check every hour if values are on current.
In PHP I loop first to get all pairnames I need from an other table. Inside of this loop, I loop through the 4 different timeframes to create this SELECT:
(
SELECT value1, timeframe
FROM db1
WHERE pairname = 'blabla'
AND timeframe = '1h'
ORDER BY value1 DESC
LIMIT 2
)
UNION ALL (
SELECT value1, timeframe
FROM db1
WHERE pairname = 'blabla'
AND timeframe = '4h'
ORDER BY value1 DESC
LIMIT 2
)
UNION ALL (
SELECT value1, timeframe
FROM db1
WHERE pairname = 'blabla'
AND timeframe = '8h'
ORDER BY value1 DESC
LIMIT 2
)
UNION ALL (
SELECT value1, timeframe
FROM db1
WHERE pairname = 'blabla'
AND timeframe = '2h'
ORDER BY value1 DESC
LIMIT 2
)
it works nicely. But because I have 290 queries out of the loop, it takes some time. (about 9 secs with all the other script part, that's ok.)
Now I do not only want the last 2 entries of each pairname/timeframe but also to count how many entries I have, I need to count each row of each pairname/timeframe.
(
SELECT COUNT( * ) AS rawnr
FROM db1
WHERE pairname = 'blabla'
AND timeframe = '1h'
)
UNION ALL (
...
)
Now I have another 290 queries. and script-time doubles. It's getting slow. Is it possible to count() and select at the same time, if I have this UNION ALL inside of my SELECT?
I've tried it, and it does not work, maybe because of the "limit 2" inside of the same select? I also tried to UNION ALL the count() into a new select, and this did not work either, because UNION wants the same amount of columns.
My solution works with about 17 seconds script-time. After this, I will have to check each result if it's up to date, pull new data from another server and then also delete/add new entries. This will definitely exceed my script-time, and I will have to find a solution for refreshing the script after certain amount of pairname/timeframe editing. (if you know how to reload a php-script at a certain point, that would be also fine!)
EDIT to see the other tables and the script:
The pairname is combined out of 2 names. Name one could be "app" and second "usd".
1st table with names formed like this:
nameID | name | active | value1 | value2 | value3
2nd table contains which names can be paired.
pairID | nameID1 | nameID2 | active | somevalue
I check first which "names" are active and then which pairs are "active". After this I combine the names to get: "appusd". This pairname I use in the third table, there I save all the data to this pairnames.
The reason I use "pairname" as a own raw is, that the data I fill in comes from an API that wants exactly this "pairname", so I can handle data better later on.
The thing is, that I do not want all names, and not all pairname-combinations.
All this has a script-time of 0,7 sec. So it's fine.
$tradeagainst = array("ABC","USD");
$timeframearr = array("1h","2h","4h","8h");
$tradeagainstsql = "";
$namepairarr = array();
$insert = "";
$ticker = array();
$tabelle = "names";
$tabelle2 = "namepairs";
$sqlsc = "SELECT NameID, shortname, nametyp
FROM ".$tabelle."
ORDER BY shortname ASC";
$resultsc = mysqli_query($conn, $sqlsc);
$nameshortnamearr = array();
$nameIDarr = array();
while($rowc = mysqli_fetch_assoc($resultsc)){
$nameshortnamearr []= $rowc["shortname"];
$nameIDarr []= $rowc["NameID"];
if(in_array($rowc["shortname"],$tradeagainst)){
if($tradeagainstsql == ""){
$tradeagainstsql .= " AND (".$tabelle2.".tradenameID = '".$rowc["NameID"]."'";
}else{
$tradeagainstsql .= " OR ".$tabelle2.".tradenameID = '".$rowc["NameID"]."'";
}
}
}
if($tradeagainstsql != ""){
$tradeagainstsql .= ") ";
}
$sqls = "SELECT ".$tabelle.".*, GROUP_CONCAT(".$tabelle2.".tradenameID) as trID
FROM ".$tabelle."
LEFT JOIN ".$tabelle2." ON ".$tabelle.".NameID = ".$tabelle2.".NameID
WHERE ".$tabelle.".active != '1'
AND ".$tabelle2.".active != '1'
".$tradeagainstsql."
GROUP BY ".$tabelle.".NameID
ORDER BY ".$tabelle.".shortname";
$results = mysqli_query($conn, $sqls);
if (mysqli_num_rows($results) > 0) {
while($row = mysqli_fetch_assoc($results)) {
$nameID = $row["NameID"];
$shortname = $row["shortname"];
$active = $row["active"];
$nametyp = $row["nametyp"];
$namepairsarr = explode(",",$row["trID"]);
foreach($namepairsarr as $trnameID){
$ct++;
$namepair = $shortname.$nameshortnamearr[array_search($trnameID, $nameIDarr)];
$namepairarr []= $namepair;
}
}
}
this first part I do, to get the $namepairarr. It is also dynamically, to be able to set up, the $tradeagainst, $timeframearr;
The second part is this one:
$tabelle3 = "namepairdata";
$updatearray = array();
foreach($namepairarr as $namepair){
$sqlnp = "";
$sqlnpcount = "";
foreach($timeframearr as $timeframe){
if($sqlnp == ""){
$sqlnp .= "(SELECT value1, timeframe
FROM ".$tabelle3."
WHERE pair = '".$namepair."' AND timeframe = '".$timeframe."' ORDER BY value1 DESC LIMIT 2)";
$sqlnpcount .= "(SELECT count(*) AS rawcount
FROM ".$tabelle3."
WHERE pair = '".$namepair."' AND timeframe = '".$timeframe."')";
}else{
$sqlnp .= " UNION ALL (SELECT value1, timeframe
FROM ".$tabelle3."
WHERE pair = '".$namepair."' AND timeframe = '".$timeframe."' ORDER BY value1 DESC LIMIT 2)";
$sqlnpcount .= " UNION ALL (SELECT count(*) AS rawcount
FROM ".$tabelle3."
WHERE pair = '".$namepair."' AND timeframe = '".$timeframe."')";
}
}
$resultnp = mysqli_query($conn, $sqlnp);
$resultnpc = mysqli_query($conn, $sqlnpcount);
$rowsvalue1 = array();
$rowstimeframe = array();
$rowscount = array();
while($rowkl = mysqli_fetch_assoc($resultnp)){
$rowsvalue1 []= $rowkl['value1'];
$rowstimeframe []= $rowkl['timeframe'];
}
while($rowklc = mysqli_fetch_assoc($resultnpc)){
$rowscount []= $rowklc['rawcount'];
}
$rc = 0;
foreach($timeframearr as $timeframe){
$value11 = "";
$value12 = "";
$timeframecount = $rowscount[$rc]; $rc++;
for($ints = 0; $ints < count($rowstimeframe); $ints++){
if($timeframe == $rowstimeframe[$ints]){
if($value11 == ""){
$value11 = $rowsvalue1[$ints];
}else{
$value12 = $rowsvalue1[$ints];
}
}
}
$updatearray []= array($namepair,$timeframe,$value11,$value12,$timeframecount);
}
Finally I get an array $updatearray, which I will have to do something with.

Mysql where clause (search where table column match) [duplicate]

This question already has answers here:
Query with multiple values in a column
(4 answers)
Closed 6 years ago.
I have 1 table, t1, around 500+ data row, I just show a sample data.
Data as below:
+--------+----------+-------------------+
| id | Name | category |
+--------+----------+-------------------+
| 1 | ABC | 6,9,25,27 |
+---------------------------------------+
My mysql query like below:
$gcategory = intval($_GET['cat']);
$test = DB::fetch_all("SELECT * FROM t1 WHERE category like '%$gcategory%' ORDER BY id DESC");
foreach($test as $te){
$list[] = $te;
}
But if $gcategory = '7'; the ABC also will appear in my $list[], but I just want when $gcategory = '6' || $gcategory = '9' || $gcategory = '25' || $gcategory = '27' then ABC only appear in my $list[]? how to fix this?
Thanks.
Please try like following way, you should use find_in_set php function when you finding from , seperated list of value:
$gcategory = intval($_GET['cat']);
$test = DB::fetch_all("SELECT * FROM t1 WHERE FIND_IN_SET("'.$gcategory.'", category) ORDER BY id DESC");
foreach($test as $te){
$list[] = $te;
}
Try using MySQL FIND_IN_SET() function
You query will be like this
"SELECT * FROM t1 WHERE FIND_IN_SET($gcategory,category) ORDER BY id DESC"

Dynamically selecting tables in mySQL

I have a query in mySQL
SELECT id FROM admin_products;
which return a list of ids, like so
+------+
| id |
+------+
| 1 |
| 2 |
| 3 |
| 4 |
| 5 |
+------+
And I was using PHP to dynamically generate tables like
vendor_1, vendor_2, vendor_3, vendor_4, vendor_5
Now I want to write a query to retrieve the price and quantity from the table id
For example
"ENTER QUERY HERE"
Should retrieve
+-----------------------------+
| id | price | quantity |
+-----------------------------+
| 1 | 23| 13| // price and quantity retrieved from table vendor_1 since id=1
| 2 | 158| 85| // price and quantity retrieved from table vendor_2 since id=2
| 3 | 15| 7| // price and quantity retrieved from table vendor_3 since id=3
| 4 | 112| 9| // price and quantity retrieved from table vendor_4 since id=4
| 5 | 123| 199| // price and quantity retrieved from table vendor_5 since id=5
+-----------------------------+
What I'm doing now in PHP is:
$conn = mysqli_connect($server,$user,$pwd,$db);
$sql = "SELECT id FROM admin_products";
$res = mysqli_query($conn,$sql);
if(mysqli_num_rows($res)>0){
while($row = mysqli_fetch_assoc($res)){
$product = array();
$innerSQL = "SELECT price,quantity FROM vendor_".$row['id'];
$innerRes = mysqli_query($conn,$innerSQL);
if(mysqli_num_rows($innerRes)>0){
while($innerRow = mysqli_fetch_assoc($innerRes)){
array_push($product,$row['id']);
array_push($product,$innerRow['price']);
array_push($product,$innerRow['quantity']);
}
}
}
}
But it takes two hits to the mySQL database. Can't it be reduced to one?
EDIT
I have later on realized that my database structure was incorrect and dynamically creating tables is a very bad idea and could spell disaster later on
-Solution 1:
Note: This will only work if you have in your vendor_x tables id for the vendor id to match them with. (As Strawberry said, this is a terrible idea to dynamically generate tables).
After selecting the correct id you can do something like this:
connect to the MySql Server
Then you can create the table name and store it in a variable.
$tableName = 'vendor_' . $id;
I would suggest after that to have a check if the table exists with a simple query:
$sql = "SHOW TABLES LIKE '$tableName'";
If this returns empty result you can throw an exception that the table does not exist or handle it whatsoever way you would like.
After checking every table, to be sure it exists, you can create your query.
$joins = "";
$sql = "
SELECT
v.id,
price,
quantity
FROM
vendors AS v
";
foreach ($ids as $id) {
$tableName = "vendor_" . $id;
$tableAlias = "v".$id;
$joins .= " LEFT JOIN " . $tableName . " AS ". $tableAlias ."
ON (v.id = ". $tableAlias .".vendor_id) ";
}
$sql .= $joins;
Then execute the query.
-Solution 2:
Create only one table to manage your vendors. It should have a structure like this :
`id` // AI value
`vendor_id` // The id of the vendor to easily join it afterwards
`price`
`quantity`
You can name it something like vendor_product or whatsoever
And now you have only one simple query:
$sql = "
SELECT
v.id,
vp.quantity,
vp.price
FROM
vendors AS v
LEFT JOIN vendor_product AS vp
ON (vp.vendor_id = v.id)
";
EDIT for the comment about the structure:
You will need one table for the vendors, such so:
`vendor`:
`id`, //AI value
`username`,
`password` // I suggest to you not to keep it in plain text.
`vendor_product` :
`id`, //AI value
`vendor_id`,
`price`,
`quantity`
I don't know here if you are going to store more information about each product, but this should do the trick.
How to show the product with least price ?
You need to match them by somehow and group by that selecting minimum price.
Try this if it suits
$table = "vendor"."_".$id; // this will create table name if $id = 1 then $table = vendor_1;
mysqli_query($connect , "SELECT * FROM $table");
2nd
If you want to fetch data of all table at once then
1) fetch id from admin_products and store in an array like
$ids = array(1,2,3,4,5);
2) Now loop throw array and create sql;
$sql = "SELECT * FROM ";
$ids = array(1,2,3,4,5);
foreach($ids as $id){
$table = "vendor"."_".$id; // this will create table name if $id = 1 then $table = vendor_1;
$sql .=" $table,";
}
$sql = rtrim($sql,",");// this will trim the last comma
echo $sql;
// output SELECT * FROM vendor_1, vendor_2, vendor_3, vendor_4, vendor_5

Mysql query array

I have two tables:
1 - hotels[id,name,extras] ( name of hotels with column extras which I've select for each one)
2 - extras[id,name] ( here's the extras of hotel like wifi,tv,swim... )
$name = $_GET['name'];
$hotels_q = mysql_query("SELECT * FROM `hotels` WHERE `name`='$name'") or die (mysql_error());
$hotels_row = mysql_fetch_array($hotels_q);
$id = $hotels_row['id'];
$extras = explode(",", $hotels_row['extras']);
$ekstras_q = mysql_query("SELECT * FROM `extras` order by id") or die(mysql_error());
While($ekstras_row = mysql_fetch_array($ekstri_q)){
$eid = $ekstras_row['id'];
$ename = $ekstri_row ['name'];
echo '<ul><li><input type="checkbox" name="extras['.$eid.'][]" value="'.$ename.'"';
if (in_array($eid, $ekstras)) echo'checked';
echo'/>'.$ename.'</li></ul>';
Problem is here extras_q displays all entries with checked ones from table, but I want only to display only checked items!
Since you're storing your extra IDs in one column as a comma separated string, I think you should be able to do this by not exploding that value, and then using the CSV string as an IN criteria in your second query.
//...
$extras = $hotels_row['extras']; // don't explode
// Use IN with the $extras CSV here
$ekstras_q = mysql_query("SELECT * FROM `extras`
WHERE id IN ($extras) ORDER BY id") or die(mysql_error());
If you are able to modify your database, you can instead create a many-to-many relationship between hotels and extras by adding a table to join those two items together instead of using a CSV column as you currently are. This can make it easier to write queries to select the related records.
If you add a third table hotel_extras with columns hotel_id and extra_id, you can insert one row for each extra that each hotel has. For example, if the hotel with id 1 has several different extras, its entries in that table would look like this:
table: hotel_extras
_______________________
| hotel_id | extra_id |
=======================
| 1 | 1 |
| 1 | 3 |
| 1 | 4 |
-----------------------
Here is an example using PDO of how you could query data from a setup like that:
$sql = "SELECT e.id, e.name
FROM hotels h
INNER JOIN hotel_extras he ON h.id = he.hotel_id
INNER JOIN extras e ON he.extra_id = e.id
WHERE h.`name` = ?";
$stmt = $pdo->prepare($sql);
$stmt->bindValue(1, $_GET['name']);
$stmt->execute();
while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
$eid = $row['id'];
$ename = $row['name'];
'<li><input type="checkbox" name="extras['.$eid.'][]" value="'.$ename.'" checked/>'.$ename.'</li>';
}

Mysql reducing number of queries

I have 3 columns in table1: book, key and value.
| book | key | value |
------------------------------
| 1 | author | a |
| 1 | editor | b |
| 1 | book | c |
Instead of runnuing three queries
$data = mysql_query("
SELECT * FROM table1 WHERE book = '1' AND key = 'author'
") or die(mysql_error());
while($info = mysql_fetch_array( $data ))
{
$value1 = $info['value'];
}
Then repeat this for editor and book.
$value1 $value2 $value3 are inserted in different places on page
Could I do this with one query?
Yes. If there are no other entries with "book = 1" you just query
SELECT * FROM table1 WHERE book = '1'
If there are more entries you can use this query:
SELECT * FROM table1 WHERE book = '1' AND key IN('author','editor','book')
And then create an assoc array:
while($info = mysqli_fetch_assoc( $data ))
{
$value[$info['key']] = $info['value'];
}
...
echo "the book {$value['book']} was written by {$value['author']}";
for create a $value array you have to use this :
$value[] = $info['value'];
instead of this :
$value1 = $info['value'];
Also you can use this code :
$value[]["key"] = $info['key'];
$value[]["value"] = $info['value'];
And for example you can call first row's value with $value[0]["value"]
If you want the values in a single record try this:
SELECT `t1`.`value` AS `value1`, `t2`.`value` AS `value2`, `t3`.`value` AS `value3`
FROM
`table1` AS `t1` CROSS JOIN
`table1` AS `t2` CROSS JOIN
`table1` AS `t3`
WHERE
(`t1`.`book` = 1) AND (`t1`.`key` = 'author')
AND (`t2`.`book` = 1) AND (`t2`.`key` = 'editor')
AND (`t3`.`book` = 1) AND (`t3`.`key` = 'book');

Categories