I've looked at the other 15 or so answers related to this and haven't come up with an answer that works. Here's the code responsible:
function count_distinct($table, $field, $from, $match, $other='Other'){
try{
$ret = array();
$query = 'SELECT '.$table.'.'.$field.', COUNT(*) AS num FROM '.$from;
$query .= ($match) ? ' WHERE '.$match : '';
$query .= ' GROUP BY `'.$table.'`.`'.$field.'`';
$q = mysqli_query($this->con, $query);
if (!$q) {
throw new Exception('Error executing query ' . $query . ' ' . mysqli_error($this->con));
} else {
...
}
return $ret;
}
catch(Exception $e){... }
}
I've removed some of the processing code which seems to work fine. The inputs for this function are code-generated, not client/user generated, and are free from white space or symbols.
I'm getting this error:
Error executing query SELECT interventions.iTYPE, COUNT(*) AS num
FROM interventions JOIN INC ON INC.form_id = interventions.form_id JOIN FORMS ON INC.form_id=FORMS.form_id WHERE `interventions`.`iCAT` = "IV/IO" AND INC.`Pt.AgeRange` <> "Manikin/Lab" AND FORMS.course_id="15" GROUP BY `interventions`.`iTYPE` Unknown column 'interventions.iTYPE' in 'field list'
The table config is this:
interventions
form_id (int (10))
iCAT (varchar(20))
iTYPE (varchar(20))
Result (tinyint(1))
INC
form_id (int(10))
Pt.AgeRange (varchar(20))
FORMS
form_id (int(10))
course_id (int(20))
etc.
The crazy part is that if I copy/paste the query printed in the error message and run it in Sequel Pro it works fine, error free, and prints the result without a problem.
I'm at my wits end—I've tried restarting the server, changing the order of the field list, removing the GROUP BY clause, adding back ticks, removing them...I'm out of ideas.
Related
I'm trying to figure out the best way to resolve inserting a conditional sql snippet within a laravel 5.8 application function.
I'm successfully calling a function with a request parameter $type.
$type coming into this function can be 1,2 or 3 (when 3 I basically want the sql query to perform with nothing in that spot), but currently it successfully returns the query results from below as if nothing is being injected at all. I can dump the $typeCondition based on the selection and it toggles properly, so I know the typeCondition is being set properly but it looks like it's not actually plugging the typeCondition into the query and I get the same results every time, so it seems like maybe this is a syntax issue?
If I run the below version as just :typeCondition it errors out and says it's not a valid param call.
What should I do here?
public static function gatherReporting($type) {
if($type ==1){
$typeCondition = 'AND T1 . parent_id IS NOT NULL';
}elseif($type ==2){
$typeCondition = 'AND T1 . parent_id IS NULL';
}else{
$typeCondition = '';
}
$sql = "
SELECT *
FROM SCHEMA . TABLE T1
WHERE ACTIVE = 1
{:typeCondition}
INNER JOIN SCHEMA . TABLE2 T2
ON T1.ID = T2.ID
";
return DB::connection('odbc')->select($sql, [
'typeCondition' => $typeCondition
]);
}
You can treat the query as a string and concatenate it with your variable. Like so:
$sql = "SELECT * FROM SCHEMA . TABLE T1 WHERE ACTIVE = 1" . $typeCondition . "INNER JOIN SCHEMA . TABLE2 T2 ON T1.ID = T2.ID";
You can use Query Builder to get something clean :
$query = Schema::select('*')->where('active', 1);
if ($type === 1) {
$query->whereNotNull('parent_id');
} elseif ($type === 2) {
$query->whereNull('parent_id');
}
$data = $query->get();
I have lost 4 hours on this thing so far, and it's very very strange. I have the following simple select:
SELECT pl.id_product, pl.link_rewrite as `product_link`
FROM ps_product_lang pl
WHERE UPPER(pl.`name`) = UPPER('HAVOLINE EXTRA SAE 10W-40')
My PHP code is the following:
$strsqlGruppe = 'SELECT pl.id_product, pl.link_rewrite as `product_link`
FROM ps_product_lang pl
WHERE UPPER(pl.`name`) = UPPER(\''.$name.'\') ';
echo('<pre>'.$strsqlGruppe.'</pre>');
$ressqlGruppe = mysqli_query($db_conn, $strsqlGruppe);
while ($row = mysqli_fetch_array($ressqlGruppe))
{
$url = $row['product_link'];
return $url;
}
There is one record that match for this $name and it does not given as result. I have tried to change the WHERE clause with "WHERE id_product=199" and it works like that. So the problem is with the WHERE clause: UPPER(pl.name) = UPPER('HAVOLINE EXTRA SAE 10W-40') .
I have tried even STRCMP too but no result. What am I doing wrong?
Here is the phpMyAdmin screenshot with the result:
I'm trying to make a blog in PHP/MySQL and have got 2 tables
posts (id int(6),cat_id int(3),title varchar(255),contents text)
categories (cat_id int(11),name varchar(24)).
I am getting an error - 'Unknown column 'id' in 'where clause'' when I try to run edit function.
<?php
include_once('resources/init.php');
$post=get_posts($_GET['id']);
if(isset($_POST['title'],$_POST['contents'],$_POST['category']))
{
$title=trim($_POST['title']);
$contents=trim($_POST['contents']);
edit_post($_GET['id'],$title,$contents,$_POST['category']);
header("Location:index.php?id=$post[0]['posts.id']");
die();
}
?>
Here is the edit function -
function edit_post($id,$title,$contents,$category)
{
$id=(int)$id;
$title=mysql_real_escape_string($title);
$category=(int)$category;
$contents=mysql_real_escape_string($contents);
mysql_query("UPDATE posts SET cat_id= {$category},
title='{$title}',
contents='{$contents}'
WHERE id={$id}");
}
You might need to refer get_posts function-
function get_posts($id=null,$cat_id=null){
$posts=array();
$query=("SELECT posts.id AS post_id, categories.cat_id AS category_id,
title, contents,
categories.name AS name
FROM posts
INNER JOIN categories ON categories.cat_id = posts.cat_id");
if (isset($id)) {
$id=(int)$id;
$query .= " WHERE posts.id={$id}";
}
if(isset($cat_id)) {
$cat_id=(int)$cat_id;
$query .=" WHERE categories.cat_id={$cat_id}";
}
$query .= " ORDER BY posts.id DESC";
$query = mysql_query($query);
while($row=mysql_fetch_assoc($query)) {
$posts[]=$row;
}
return $posts;
}
I have referred the solutions provided for this type of error on the site but it wasn't helpful for my case. Please help.
check if Id is set in your url, try this approach
include_once('resources/init.php');
if(isset($_GET['id'],$_POST['title'],$_POST['contents'],$_POST['category']))
{
$post=get_posts($_GET['id']);
$title=trim($_POST['title']);
$contents=trim($_POST['contents']);
edit_post($_GET['id'],$title,$contents,$_POST['category']);
$id = $post[0]['posts.id'];
echo $id;
header("Location:index.php?id=".$id);
die();
}
You shouldn't run your first script directly, it should be runned with form submit with id input inside.
I'm running a query on a single table. I need the end result to be an array of rows where the current user is listed as a participant in the message that the DB row records. The two participants columns are staff and clients, and the contain serialized arrays of user IDs and/or role name (e.g., a:3:{i:0;s:8:"staffman";i:1;s:3:"203";i:2;s:3:"170";}).
Would it be faster to try to filter out any non-matches for the current user all in the SQL query, or to do a catch-all query (maybe on several hundred rows), then loop through them to filter out the ones where the user is not a participant?
SQL option:
$query = "SELECT * FROM ".self::$messages;
if($is_client) $query .= " WHERE type = 'clients'";
elseif($is_staff) $query .= " WHERE type = 'staff' AND (staff LIKE '%\"".$user->ID."\"%' OR staff LIKE 'staff\"%')";
elseif(!$is_admin)
{
$query .= " WHERE type = 'staff' AND (staff LIKE '%\"".$user->ID."\"%' OR staff LIKE 'staff\"%' OR staff LIKE 'clientman%'";
if($is_staffman) $query .= " OR staff LIKE 'staffman%'";
if($is_accountant) $query .= " OR staff like 'accountant%'";
$query .= ")";
}
$query .= " ORDER BY updated DESC";
LOOP Option:
$col = $is_client ? 'clients' : 'staff';
$query = "SELECT * FROM ".self::$messages." WHERE type = {$col} ORDER BY updated DESC";
// do the query, then check if there are results. if results...
foreach($results as $msg)
{
if(empty($msg->$col)) continue;
$users = unserialize($msg->$col);
if($is_client)
{
if(!in_array($user->ID, $users) && !in_array('client', $users)) continue;
}
elseif(!$is_admin && $msg->created_by != $user->ID)
{
if(!in_array($user->ID, $users))
{
if(!in_array('staff', $users))
{
if(!$is_clientman || !in_array('clientman', $users))
{
if(!$is_staffman || !in_array('staffman', $users))
{
if(!$is_accountant || !in_array('accountant', $users)) continue;
}
}
}
}
}
// match found, do stuff with it here
}
You should always use SQL option in such case.
$query = "SELECT * FROM ".self::$messages;
if($is_client) $query .= " WHERE type = 'clients'";
elseif($is_staff) $query .= " WHERE type = 'staff' AND (staff LIKE '%\"".$user->ID."\"%' OR staff LIKE 'staff\"%')";
elseif(!$is_admin)
{
$query .= " WHERE type = 'staff' AND (staff LIKE '%\"".$user->ID."\"%' OR staff LIKE 'staff\"%' OR staff LIKE 'clientman%'";
if($is_staffman) $query .= " OR staff LIKE 'staffman%'";
if($is_accountant) $query .= " OR staff like 'accountant%'";
$query .= ")";
}
$query .= " ORDER BY updated DESC";
Why ?
1.See you already get less records(those are Needed only) by filtering thru WHERE clause.There is no point in fetching all the records and filtering it in php.
2.See its headache to manage code in PHP as it got complicated.
3.Less no.of lines.
4.It is managed by SQL most of the time, php logic will make it slow.
I've accepted the answer above because it was a clear and definitive answer to the question I posed. However, Stuart (in the comments) suggested I try to normalize my structure rather than scan serialized arrays with LIKES and the like. So that's what I've done.
Instead of saving my participants array in serialized form in the same table as the messages table, I created a new table called message_recipients with just three columns (id,mid,user), where mid refers to the id column from the messages table, and where user stores the individual user id or role name.
So the trickiest part (which wasn't really that tricky) was how to manage adding and deleting rows from the message_recipients table when the user changes the allowed recipients for a given message. (In the form, they have two multiselect dropdowns—one for staff, one for clients).
So here's how I managed the updated/changing participants part:
For each field (staff and clients), I pass my function two arrays, one with the new participants and one with the old participants. (If the message is being created, then the old participants array is simply empty.)
Then here's the function (basically just four lines of actual work):
public function update_participants($mid = false, $new_staff = array(), $old_staff = array(), $new_clients = array(), $old_clients = array())
{
global $wpdb;
if(empty($mid)) return array();
$add = array_filter(array_unique(array_merge(array_diff($new_staff, $old_staff), array_diff($new_clients, $old_clients))));
$delete = array_filter(array_unique(array_merge(array_diff($old_staff, $new_staff), array_diff($old_clients, $new_clients))));
if(!empty($add)) $wpdb->query("INSERT INTO ".self::$participants." (mid,user) VALUES(".$mid.",\"".implode("\"),(".$mid.",\"", $add)."\")");
if(!empty($delete)) $wpdb->query("DELETE FROM ".self::$participants." WHERE mid = {$mid} AND user IN (\"".implode("\", \"", $delete)."\")");
return array('added'=>$add, 'deleted'=>$delete);
}
Then I return the resulting arrays of people being added or deleted, so I can play with that in my email notifications stuff.
Then, now that we're normalized, the original problem of querying the table to get all the messages in which the current user is a participant is much faster and simpler:
$query = 'SELECT DISTINCT a.* FROM '.self::$messages.' a, '.self::$participants.' b';
if(!$is_admin)
{
$query .= ' WHERE (b.user = '.$user->ID;
if($is_client) $query .= ' OR b.user = "client"';
elseif($is_staff) $query .= ' OR b.user = "staff"';
else
{
if($is_clientman) $query .= ' OR b.user = "clientman"';
if($is_staffman) $query .= ' OR b.user = "staffman"';
if($is_accountant) $query .= ' OR b.user = "accountant"';
$query .= ' OR a.created_by = '.$user->ID;
}
$query .= ') AND b.mid = a.id';
}
$query .= ' ORDER BY a.updated DESC';
Admins get everything, and everyone else only get any messages where their user id or their user role is found in the participants table. (Some roles have multiple roles, hence the triple if statements, rather than elseifs.
I've tested it with every possible configuration with different users. Works like a charm.
And no LIKEs, no wildcards, and no PHP loops.
Thanks everyone for the help.
I need to count the number of rows from different(!) tables and save the results for some kind of statistic. The script is quite simple and working as expected, but I'm wondering if it's better to use a single query with (in this case) 8 subqueries, or if I should use separate 8 queries or if there's even a better, faster and more advanced solution...
I'm using MySQLi with prepared statements, so the single query could look like this:
$sql = 'SELECT
(SELECT COUNT(cat1_id) FROM `cat1`),
(SELECT COUNT(cat2_id) FROM `cat2`),
(SELECT COUNT(cat2_id) FROM `cat2` WHERE `date` >= DATE(NOW())),
(SELECT COUNT(cat3_id) FROM `cat3`),
(SELECT COUNT(cat4_id) FROM `cat4`),
(SELECT COUNT(cat5_id) FROM `cat5`),
(SELECT COUNT(cat6_id) FROM `cat6`),
(SELECT COUNT(cat7_id) FROM `cat7`)';
$stmt = $db->prepare($sql);
$stmt->execute();
$stmt->bind_result($var1, $var2, $var3, $var4, $var5, $var6, $var7, $var8);
$stmt->fetch();
$stmt->free_result();
$stmt->close();
while the seperate queries would look like this (x 8):
$sql = 'SELECT
COUNT(cat1_id)
FROM
`cat1`';
$stmt = $db->prepare($sql);
$stmt->execute();
$stmt->bind_result($var1);
$stmt->fetch();
$stmt->free_result();
$stmt->close();
so, which would be faster or "better style" related to this kind of query (e.g. statistics, counter..)
My inclination is to put queries into the FROM rather than the SELECT, where possible. In this example, it requires a cross join between the tables:
select c1.val, c2.val . . .
from (select count(cat1_id) as val from cat1) c1 cross join
(select count(cat2_id as val from cat2) c2 cross join
. . .
The performance should be the same. However, the advantage appears with your cat2 table:
select c1.val, c2.val, c2.valnow, . . .
from (select count(cat1_id) as val from cat1) c1 cross join
(select count(cat2_id) as val
count(case when date >= date(now()) then cat2_id end)
from cat2
) c2 cross join
. . .
You get a real savings here by not having to scan the table twice to get two values. This also helps when you realize that you might want to modify queries to return more than one value.
I believe the cross join and select-within-select would have the same performance characteristics. The only way to really be sure is to test different versions.
The better way, is use just one query, because is only one conecction with database, instead of, if you use many queries, then are many conecctions with database, this process involves: coneccting and disconeccting, and this is more slower.
Just to follow up your comment, here is an example using one of my DBs. Using a prepared statement here buys you nothing. This multiple query in fact only executes one RPC to the D/B engine. All of the other calls are local to the PHP runtime system.
$db = new mysqli('localhost', 'user', 'password', 'blog');
$table = explode( ' ', 'articles banned comments config language members messages photo_albums photos');
foreach( $table as $t ) {
$sql[] = "select count(*) as count from blog_$t";
}
if ($db->multi_query( implode(';',$sql) )) {
foreach( $table as $t ) {
if ( ($rs = $db->store_result() ) &&
($row = $rs->fetch_row() ) ) {
$result[$t] = $row[0];
$rs->free();
$db->next_result(); // you must execute one per result set
}
}
}
$db->close();
var_dump( $result );
Just out of interest, I did an strace on this and the relevant four lines are
16:54:09.894296 write(4, "\211\1\0\0\3select count(*) as count fr"..., 397) = 397
16:54:09.895264 read(4, "\1\0\0\1\1\33\0\0\2\3def\0\0\0\5count\0\f?\0\25\0\0\0\10\201"..., 16384) = 544
16:54:09.896090 write(4, "\1\0\0\0\1", 5) = 5
16:54:09.896192 shutdown(4, 2 /* send and receive */) = 0
There was ~1 mSec between the query and the response to and from the MySQLd process (this is because this was on localhost, and the results were in its query cache, BTW).. and 0.8 mSec later the DB close was executed. And that's on my 4-yr old laptop.
Regarding to TerryE's example and the advice to use multi_query(!), I checked the manual and changed the script to fit my needs.. finally I got a solution that looks like this:
$sql = 'SELECT COUNT(cat1_id) as `cat1` FROM `cat1`;';
$sql .= 'SELECT COUNT(cat2_id) as `cat2` FROM `cat2`;';
$sql .= 'SELECT COUNT(cat2_id) as `cat2_b` FROM `cat2` WHERE `date` >= DATE(NOW());';
$sql .= 'SELECT COUNT(cat3_id) as `cat3` FROM `cat3`;';
$sql .= 'SELECT COUNT(cat4_id) as `cat4` FROM `cat4`;';
$sql .= 'SELECT COUNT(cat5_id) as `cat5` FROM `cat5`;';
$sql .= 'SELECT COUNT(cat6_id) as `cat6` FROM `cat6`;';
$sql .= 'SELECT COUNT(cat7_id) as `cat7` FROM `cat7`;';
if ($db->multi_query($sql))
{
do
{
if ($stmt = $db->store_result())
{
while ($row = $stmt->fetch_assoc())
{
foreach ($row as $key => $value)
{
$count[$key] = $value;
}
}
$stmt->free_result();
}
} while ($db->more_results() && $db->next_result());
}
There are some differences to TerryE's example, but the result is the same. I'm aware that there are 7 line at the beginning that are almost identical, but as soon as I need a WHERE clause or something else, I prefer this solution to a foreach loop where I'd need to add queries manually or use exceptions with if { ... } ...
As far as I can see, there should be no problem with my solution, or did I miss something?