I have the following PHP code that works perfectly ($qry_str is actually generated in the PHP):
$qry_str = <<<'QRY'
FIND_IN_SET('6-47', attributes)
AND FIND_IN_SET('4-176', attributes)
AND FIND_IN_SET('9-218', attributes)
QRY;
$pdo->query('DROP TEMPORARY TABLE IF EXISTS `temp_attr`');
$temp_sql = <<<"TEMP"
CREATE TEMPORARY TABLE IF NOT EXISTS `temp_attr` (
SELECT product_id, GROUP_CONCAT(CONCAT(group_id, '-', IF (custom_value != '', custom_value, value_id)) SEPARATOR ',') AS attributes
FROM `products_attributes`
GROUP BY `product_id`
HAVING $qry_str
);
TEMP;
$pdo->query($temp_sql);
$sql = "SELECT
m.recommended_price AS msrp,
m.purchase_price AS cost,
pp.USD AS regular_price,
pc.USD AS special_price,
pc.start_date AS start_date,
pc.end_date AS end_date,
pl.permalink AS permalink,
pi.name AS description,
m.sku AS sku,
m.default_category_id AS cat,
m.id AS prod_id
FROM `products` AS m
LEFT JOIN `products_prices` AS pp ON m.id = pp.product_id
LEFT JOIN `products_campaigns` AS pc ON m.id = pc.product_id
LEFT JOIN `permalinks` AS pl ON (m.id = pl.resource_id AND pl.resource = 'product')
LEFT JOIN `products_info` AS pi ON (m.id = pi.product_id)
LEFT JOIN `products_to_categories` AS ptc ON (m.id = ptc.product_id)
INNER JOIN `temp_attr` AS pa
WHERE ptc.category_id = :cat
AND m.status = 1
AND m.id = pa.product_id
LIMIT 55;
";
$data = $pdo->prepare($sql)
->bindValue('cat', 100)
->execute()
->fetchAll();
However, when I use a placeholder in the temp table code, i.e.
$temp_sql = <<<"TEMP"
CREATE TEMPORARY TABLE IF NOT EXISTS `temp_attr` (
SELECT product_id, GROUP_CONCAT(CONCAT(group_id, '-', IF (custom_value != '', custom_value, value_id)) SEPARATOR ',') AS attributes
FROM `products_attributes`
GROUP BY `product_id`
HAVING :qry_str
);
TEMP;
$sth = $pdo->prepare($temp_sql);
$sth->bindValue('qry_str', $qry_str, PDO::PARAM_STR);
$sth->execute();
I get the following error:
PHP Fatal error: Uncaught PDOException: SQLSTATE[22007]: Invalid datetime format: 1292 Truncated incorrect DOUBLE value: 'FIND_IN_SET('6-47', attributes)
AND FIND_IN_SET('4-176', attributes)
AND FIND_IN_SET('9-218', attributes)
AND FIND_IN_SET(...'
There are no datetime columns in this table.
group_id and value_id are integer columns
Since the code works fine without the placeholder, I'm at a loss as to why the use of a placeholder breaks the code. The placeholder in the main SQL works properly.
PHP 8.0
https://dev.mysql.com/doc/refman/8.0/en/prepare.html explains:
Parameter markers can be used only where data values should appear, not for SQL keywords, identifiers, and so forth.
In your case, you're apparently trying to bind an expression with the FIND_IN_SET() function. You can't do that. All expressions and other SQL syntax must be fixed in the query at the time you prepare it. You can use a parameter only in a place where you would otherwise use a scalar literal value. That is, a quoted string or a numeric literal.
Related
I'm trying to join two strings together in a prepare statement (table name & column which is dynamic) but I am getting
Uncaught PDOException: SQLSTATE[42S22]: Column not found: 1054 Unknown column 'exp.$getSkill' in 'field list'
My code is:
$query = $db->prepare('SELECT members.*, exp.*, lvl.*, exp.$getSkill AS exp_skill, lvl.$getSkill AS level_skill FROM members INNER JOIN exp ON members.id = exp.member_id INNER JOIN lvl ON members.id = lvl.member_id ORDER BY lvl.$getSkill DESC, exp.$getSkill DESC');
$query->execute();
$row = $query->fetch();
Is there a way I can join exp. and $getSkill properly?
Variable aren't evaluated inside strings with single quotes. You'd need to use double quotes, and surround the name with curly braces ({}):
$query = $db->prepare("SELECT members.*, exp.*, lvl.*, exp.${getSkill} AS exp_skill, lvl.$getSkill AS level_skill FROM members INNER JOIN exp ON members.id = exp.member_id INNER JOIN lvl ON members.id = lvl.member_id ORDER BY lvl.$getSkill DESC, exp.$getSkill DESC");
Mandatory note:
Using string concatenation in an SQL query could potentially expose your code to SQL Ijection attacks. You should be very careful if you intend to use such a technique.
Try something like this:
$query = 'SELECT members.*,
exp.*,
lvl.*,
exp.'.$getSkill.' AS exp_skill,
lvl.$getSkill AS level_skill
FROM members
INNER JOIN exp ON members.id = exp.member_id
INNER JOIN lvl ON members.id = lvl.member_id
ORDER BY lvl.$getSkill DESC, exp.$getSkill DESC';
$query = $db->prepare($query);
$query->execute();
$row = $query->fetch();
I have 2 variables to define a price range for a query. The problem I'm trying to solve is when these are not set in which case I want to show all rows (from 1, if the lower boundary is null, and to max(price) if the upper boundary is null).
I've tried with ifnull, but without success.
$priceFrom = $_POST['priceFrom'];
$priceTo = $_POST['priceTo'];
if(is_null($priceFrom) || is_null($priceTo)){
$priceFrom = 0;
$priceTo = 0;
}
$mass = array();
foreach($data as $current){
$sql = "SELECT p.price,
p.type,
p.area,
p.floor,
p.construction,
p.id as propertyID,
CONCAT(u.name, ' ',u.family) as bname,
p.type as ptype,
n.name as neighborhoodName,
CONCAT(o.name,' ',o.surname,' ',o.family) as fullName
FROM `property` p
LEFT JOIN `neighbour` n ON p.neighbour = n.id
RIGHT JOIN `owners` o ON p.owner = o.id
LEFT JOIN users u ON p.broker = u.id
WHERE `neighbour`= '$current'
AND `price` BETWEEN ifnull('$priceFrom', '1') AND ifnull('$priceTo','2000000')
";}
SQL INJECTION
^ Please Google that! Your code is seriously vulnerable! Your data can be stolen or deleted...
You have to sanitize your inputs at least with mysqli_real_escape_string()
Even better would be to take proper countermeasures to SQL injection and use prepared statements and parametrized queries! (as shown in the code below)
I think the best approach would be to handle the logic by altering the query based on the values of the variables:
$sql = "SELECT p.price,
p.type,
p.area,
p.floor,
p.construction,
p.id as propertyID,
CONCAT(u.name, ' ',u.family) as bname,
p.type as ptype,
n.name as neighborhoodName,
CONCAT(o.name,' ',o.surname,' ',o.family) as fullName
FROM `property` p
LEFT JOIN `neighbour` n ON p.neighbour = n.id
RIGHT JOIN `owners` o ON p.owner = o.id
LEFT JOIN users u ON p.broker = u.id
WHERE `neighbour`= :current "; //note: ending white space is recommended
//lower boundary clause -- if variable null - no restriction
if(!is_null($priceFrom){
sql = sql . " AND `price` >= :priceFrom "; // note: whitespace at end and beginning recommended
}
//upper boundary -- better than to set it to an arbitrary "high" value
if(!is_null($priceTo)){
sql = sql . " AND `price` <= :priceTo "; // note: whitespace at end and beginning recommended
}
This approach allows for any upper value: if there is a serious inflation, a different currency, or suddenly the code will be used to sell housese and there will be products with prices > 200000, you don't need to go out and change a lot of code to make it show...
The parameters need to be bound when executing the query of course:
$stmt = $dbConnection->prepare(sql);
$stmt->bind_param('current', $current);
if(!is_null($priceFrom)){
$stmt->bind_param('priceFrom', $priceFrom);
}
if(!is_null($priceTo)){
$stmt->bind_param('priceTo', $priceTo);
}
//execute and process in same way
$stmt->execute();
Also note: from your code it seems you are issuing queries in a loop. That is bad practice. If the data on which you loop comes
from the DB --> use a JOIN
from an array or other place of the code --> better use an IN clause for the elements
to fetch all data with one query. This helps a lot both in organizing and maintaining the code and results generally in better performance for the most cases.
I'm working with a mysql query to select data from multiple tables using LEFT OUTER JOIN. Now i get the following error when i exequte the query:
You have an error in your SQL syntax; check the manual that
corresponds to your MySQL server version for the right syntax to use
near 'wg.werkbon_global_id = wk.werkbon_klant_globalid LEFT OUTER
JOIN users AS u' at line 16
Only the problem is that i can't find out what's wrong with my query.
PHP Query:
$query = '
SELECT
wg.werkbon_global_id AS id,
wg.werkbon_global_status AS status,
wg.werkbon_global_date_lastedit AS date,
usr.user_firstname AS monteur_vn,
usr.user_insertion AS monteur_tv,
usr.user_lastname AS monteur_an,
wg.werkbon_global_type AS type,
wg.werkbon_global_layout AS layout,
wg.werkbon_global_werkzaamheden AS werkzaamheden,
wg.werkbon_global_opmerkingen AS opmerkingen,
wk.werkbon_klant_nummer AS klantnr
FROM
werkbon_klant AS wk
LEFT OUTER JOIN werkbon_global AS wg
wg.werkbon_global_id = wk.werkbon_klant_globalid
LEFT OUTER JOIN users AS usr
usr.user_id = wg.werkbon_global_monteur_finish
WHERE
wk.werkbon_klant_nummer = '.$db->Quote($klantid).'
ORDER BY id ASC;
$result = $db->loadAssoc($query);
I think my problem has something todo with left outer join but what?
You are missing the ON operator in your joins!
The correct syntax for a join is:
SELECT * FROM x LEFT JOIN y ON condition WHERE...
$query = "
SELECT
wg.werkbon_global_id AS id,
wg.werkbon_global_status AS status,
wg.werkbon_global_date_lastedit AS date,
usr.user_firstname AS monteur_vn,
usr.user_insertion AS monteur_tv,
usr.user_lastname AS monteur_an,
wg.werkbon_global_type AS type,
wg.werkbon_global_layout AS layout,
wg.werkbon_global_werkzaamheden AS werkzaamheden,
wg.werkbon_global_opmerkingen AS opmerkingen,
wk.werkbon_klant_nummer AS klantnr
FROM
werkbon_klant AS wk
LEFT OUTER JOIN werkbon_global AS wg
wg.werkbon_global_id = wk.werkbon_klant_globalid
LEFT OUTER JOIN users AS usr
usr.user_id = wg.werkbon_global_monteur_finish
WHERE
wk.werkbon_klant_nummer = '.$db->Quote($klantid).'
ORDER BY id ASC";
$result = $db->loadAssoc($query);
Make sure there isn't missing quote
Problem soved thanks to arkascha
The fixed query is now:
$query = '
SELECT
wg.werkbon_global_id AS id,
wg.werkbon_global_status AS status,
wg.werkbon_global_date_lastedit AS date,
usr.user_firstname AS monteur_vn,
usr.user_insertion AS monteur_tv,
usr.user_lastname AS monteur_an,
wg.werkbon_global_type AS type,
wg.werkbon_global_layout AS layout,
wg.werkbon_global_werkzaamheden AS werkzaamheden,
wg.werkbon_global_opmerkingen AS opmerkingen,
wk.werkbon_klant_nummer AS klantnr
FROM
werkbon_klant AS wk
LEFT OUTER JOIN werkbon_global AS wg ON
wg.werkbon_global_id = wk.werkbon_klant_globalid
LEFT OUTER JOIN users AS usr ON
usr.user_id = wg.werkbon_global_monteur_finish
WHERE
wk.werkbon_klant_nummer = '.$db->Quote($klantid).'
ORDER BY id ASC';
$result = $db->loadAssoc($query);
#fred i don't need to add quotes by column names. You only need to add quotes by string/blob values.
#johny my $db->Quote() function will add qoutes automaticly. I don't need to add them and put everything in quote's.
Thanks all for help.
At the moment i have this query:
$qry = "SELECT platforms.PID
FROM user_profile
LEFT JOIN platforms
ON platforms.relaccount = user_profile.subkey
WHERE user_profile.UID = `".$data['id']."`";
$games = $this->db->select('main.APPID, games_other.name, games_other.logo')
->select('platforms.PID, platforms.name AS pname, platforms.logo AS plogo')
->select('('.$qry.') AS filt', null, FALSE)
->from('games_link AS main')
->join('games_platforms', 'games_platforms.APPID = main.APPID', 'left')
->join('platforms', 'platforms.PID = games_platforms.PID', 'left')
->join('games_other', 'games_other.APPID = main.GB_ID', 'left')
->like('games_other.name', $name)
->where('platforms.PID', 'filt')
->limit(15)
->get();
Where im trying to get games based on an input string but filtered by what platforms a user has, but it returns this error:
Unknown column 'Cf9nHvOlaaLzFRegX2Il' in 'where clause'
SELECT `main`.`APPID`, `games_other`.`name`, `games_other`.`logo`, `platforms`.`PID`, `platforms`.`name` AS pname, `platforms`.`logo` AS plogo, (SELECT platforms.PID FROM user_profile LEFT JOIN platforms ON platforms.reaccount = user_profile.subkey WHERE user_profile.UID = `Cf9nHvOlaaLzFRegX2Il`) AS filt FROM (`games_link` AS main) LEFT JOIN `games_platforms` ON `games_platforms`.`APPID` = `main`.`APPID` LEFT JOIN `platforms` ON `platforms`.`PID` = `games_platforms`.`PID` LEFT JOIN `games_other` ON `games_other`.`APPID` = `main`.`GB_ID` WHERE `platforms`.`PID` = 'filt' AND `games_other`.`name` LIKE '%a%' LIMIT 15
Filename: response/update.php
I have tried changing a few things around but nothing fixes this.
Also since I Havant been able to get there yet, would this work as a filter. The sub query will return multiple.
You have a column value with backticks and its not valid it should be single quote or no quote if its INTEGER
$qry = "SELECT platforms.PID
FROM user_profile
LEFT JOIN platforms
ON platforms.reaccount = user_profile.subkey
WHERE user_profile.UID = '".$data['id']."'";
Your problem is here:
WHERE user_profile.UID = `Cf9nHvOlaaLzFRegX2Il`
Backticks, `, escape database objects (view names, table names, column names ... etc).
String values should be escaped with single quotes, '.
I would be wary of concatenating in values, you should probably bind them in if possible.
I need to fetch the following information about indices on a specific table:
index name
columns that are indexed
unique or not?
How can I do that in PostgreSQL 8.4?
NOTE: I have to be able to call this stuff with PHP. Just saying...
EDIT: I first had this query, but it only works starting with PostgreSQL 9.0:
SELECT t.relname AS table_name,
relname AS index_name,
a.attname AS column_name,
ix.indisunique
FROM pg_class t,
pg_class i,
pg_index ix,
pg_attribute a,
pg_constraint c
WHERE t.oid = ix.indrelid
AND i.oid = ix.indexrelid
AND a.attrelid = t.oid
AND i.oid = c.conindid
AND a.attnum = ANY(ix.indkey)
AND c.contype != 'p'
AND t.relkind = 'r'
AND t.relname = 'tablename'
ORDER BY t.relname, i.relname
You could simply use pg_indexes which will include the full CREATE TABLE statement (and therefor the information about the columns and the uniqueness).
Alternatively, the following should work:
select t.relname as table_name,
ix.relname as index_name,
array_to_string(array_agg(col.attname), ',') as index_columns,
i.indisunique
from pg_index i
join pg_class ix on ix.oid = i.indexrelid
join pg_class t on t.oid = i.indrelid
join (select ic.indexrelid,
unnest(ic.indkey) as colnum
from pg_index ic) icols on icols.indexrelid = i.indexrelid
join pg_attribute col on col.attrelid = t.oid and col.attnum = icols.colnum
where t.relname = 'tablename'
group by t.relname, ix.relname, i.indisunique
order by t.relname,
ix.relname
It doesn't return the columns in the correct order though. But I didn't have time to dig deeper into that.