This is my native query:
$query = <<<Query
SET #num = 0;
SET #type = 0;
SELECT md.*, od.id AS order_detail_id, od.erc AS od_ec,
#num := if(#type = od.id, #num + 1, 1) AS row_number,
#type := od.id AS dummy
FROM bar md
LEFT JOIN foobar mb ON md.mbi = mb.id
LEFT JOIN barfoo od ON od.mbid = mb.id
WHERE od.id IN ($where)
AND (od.status = $statusQueued OR od.status = $statusProcessing)
GROUP BY md.id
HAVING row_number <= od.erc
ORDER BY md.qt ASC
LIMIT 0, 1000
Query;
$nativeQuery = $this->_em->createNativeQuery($query, $rsm);
When I execute it, PDO gets upset and throws me an error:
[PDOException] SQLSTATE[HY000]: General error
No more informations.
However when I get rid of first two lines (with "SET") the query runs (however returns wrong results, as the variables must be initialised in this case). How can I force PDO to run this correctly? (when run via PMA it works just fine)
It seems that most elegant solution is to leverage MySQL alternative initialisation syntax:
$query = <<<Query
SELECT md.*, od.id AS order_detail_id, od.erc AS od_ec,
#num := if(#type = od.id, #num + 1, 1) AS row_number,
#type := od.id AS dummy
FROM (SELECT #num := 0, #type := 0) init, bar md
LEFT JOIN foobar mb ON md.mbi = mb.id
LEFT JOIN barfoo od ON od.mbid = mb.id
WHERE od.id IN ($where)
AND (od.status = $statusQueued OR od.status = $statusProcessing)
GROUP BY md.id
HAVING row_number <= od.erc
ORDER BY md.qt ASC
LIMIT 0, 1000
Query;
I ran into the same kind of issue trying to run several queries in one call (in my case it was about using SQL_CALC_FOUND_ROWS and its buddy FOUND_ROWS()).
Doctrine can't handle this. I don't know the official reason, but in my opinion it's probably a security concern (as the EM's calls are then open to basic query injection).
Still in my opinion, the most elegant way of dealing with this is using the EM's transactional mode:
//fetch PDO-ish adapter
$conn = $em->getConnection();
//open an atomic sequence
$conn->beginTransaction();
//do your stuff
$conn->executeQuery('SET #...');
$stmt = $conn->executeQuery('SELECT stuff using #vars');
//commit the atomic sequence
$conn->commit();
//fetch the result, could be useful ;)
$data = $conn->fetchAll();
Hope this helps! (and works in your case...)
Related
I currently have the below query that uses a UNION join to connect two queries and SQL variables to alternate the order the rows are shown between sum of numbers and count of activities. When I add the SET lines to the query the PHP file has an error and when I remove them the query runs but no values are retrieved. I've run the query on the server and it works.
How do I use SQL variables in the prepared statement? If it's not possible how would I rewrite the query to get the same outcome?
SET #a = 0;
SET #b = 0;
SELECT * FROM(
SELECT #a := #a + 1 AS sortOne, 1 AS sortTwo, tbl_company.comp_name AS comp_name, tbl_shift.shift_name AS shift_name,
SUM(tbl_short_term_programme.sun_p) AS sun_p_total, SUM(tbl_short_term_programme.sun_a) AS sun_a_total,
SUM(tbl_short_term_programme.mon_p) AS mon_p_total, SUM(tbl_short_term_programme.mon_a) AS mon_a_total,
SUM(tbl_short_term_programme.tue_p) AS tue_p_total, SUM(tbl_short_term_programme.tue_a) AS tue_a_total,
SUM(tbl_short_term_programme.wed_p) AS wed_p_total, SUM(tbl_short_term_programme.wed_a) AS wed_a_total,
SUM(tbl_short_term_programme.thu_p) AS thu_p_total, SUM(tbl_short_term_programme.thu_a) AS thu_a_total,
SUM(tbl_short_term_programme.fri_p) AS fri_p_total, SUM(tbl_short_term_programme.fri_a) AS fri_a_total,
SUM(tbl_short_term_programme.sat_p) AS sat_p_total, SUM(tbl_short_term_programme.sat_a) AS sat_a_total
FROM tbl_short_term_programme
INNER JOIN tbl_phase ON tbl_short_term_programme.phase_id = tbl_phase.phase_id
INNER JOIN tbl_company ON tbl_short_term_programme.company_id = tbl_company.company_id
INNER JOIN tbl_shift ON tbl_short_term_programme.shift_id = tbl_shift.shift_id
WHERE tbl_phase.phase_hash = ? AND YEARWEEK(tbl_short_term_programme.short_term_date, 2) = YEARWEEK(?, 2)
GROUP BY tbl_shift.shift_id, tbl_company.comp_name
UNION
SELECT #b := #b + 1 AS sortOne, 2 AS sortTwo, tbl_company.comp_name AS comp_name, tbl_shift.shift_name AS shift_name,
COUNT(tbl_short_term_programme.sun_p) AS sun_p_total, COUNT(tbl_short_term_programme.sun_a) AS sun_a_total,
COUNT(tbl_short_term_programme.mon_p) AS mon_p_total, COUNT(tbl_short_term_programme.mon_a) AS mon_a_total,
COUNT(tbl_short_term_programme.tue_p) AS tue_p_total, COUNT(tbl_short_term_programme.tue_a) AS tue_a_total,
COUNT(tbl_short_term_programme.wed_p) AS wed_p_total, COUNT(tbl_short_term_programme.wed_a) AS wed_a_total,
COUNT(tbl_short_term_programme.thu_p) AS thu_p_total, COUNT(tbl_short_term_programme.thu_a) AS thu_a_total,
COUNT(tbl_short_term_programme.fri_p) AS fri_p_total, COUNT(tbl_short_term_programme.fri_a) AS fri_a_total,
COUNT(tbl_short_term_programme.sat_p) AS sat_p_total, COUNT(tbl_short_term_programme.sat_a) AS sat_a_total
FROM tbl_short_term_programme
INNER JOIN tbl_phase ON tbl_short_term_programme.phase_id = tbl_phase.phase_id
INNER JOIN tbl_company ON tbl_short_term_programme.company_id = tbl_company.company_id
INNER JOIN tbl_shift ON tbl_short_term_programme.shift_id = tbl_shift.shift_id
WHERE tbl_phase.phase_hash = ? AND YEARWEEK(tbl_short_term_programme.short_term_date, 2) = YEARWEEK(?, 2)
GROUP BY tbl_shift.shift_id, tbl_company.comp_name
) AS result ORDER BY sortOne, sortTwo
$stmt->bind_param("ssss", $phase_hash, $formatted_date, $phase_hash, $formatted_date);
$stmt->execute();
$stmt->store_result();
$stmt->bind_result($comp_name, $shift_name, $sun_p_total, $sun_a_total, $mon_p_total, $mon_a_total, $tue_p_total,
$tue_a_total, $wed_p_total, $wed_a_total, $thu_p_total, $thu_a_total, $fri_p_total, $fri_a_total,
$sat_p_total, $sat_a_total);
$row_array = array();
while($stmt->fetch()) {
$tmp = array();
$tmp["shift_name"] = $shift_name;
$tmp["comp_name"] = $comp_name;
$tmp["sun_p_total"] = $sun_p_total;
$tmp["sun_a_total"] = $sun_a_total;
$tmp["mon_p_total"] = $mon_p_total;
$tmp["mon_a_total"] = $mon_a_total;
$tmp["tue_p_total"] = $tue_p_total;
$tmp["tue_a_total"] = $tue_a_total;
$tmp["wed_p_total"] = $wed_p_total;
$tmp["wed_a_total"] = $wed_a_total;
$tmp["thu_p_total"] = $thu_p_total;
$tmp["thu_a_total"] = $thu_a_total;
$tmp["fri_p_total"] = $fri_p_total;
$tmp["fri_a_total"] = $fri_a_total;
$tmp["sat_p_total"] = $sat_p_total;
$tmp["sat_a_total"] = $sat_a_total;
array_push($row_array, $tmp);
}
$stmt->close();
echo json_encode($row_array);
As Bill Karwin said, you can only run one query in a prepared statement. Another way to solve it is to assign the variables in a subquery that you join with the main query.
SELECT * FROM(
SELECT #a := #a + 1 AS sortOne, 1 AS sortTwo, tbl_company.comp_name AS comp_name, tbl_shift.shift_name AS shift_name,
SUM(tbl_short_term_programme.sun_p) AS sun_p_total, SUM(tbl_short_term_programme.sun_a) AS sun_a_total,
SUM(tbl_short_term_programme.mon_p) AS mon_p_total, SUM(tbl_short_term_programme.mon_a) AS mon_a_total,
SUM(tbl_short_term_programme.tue_p) AS tue_p_total, SUM(tbl_short_term_programme.tue_a) AS tue_a_total,
SUM(tbl_short_term_programme.wed_p) AS wed_p_total, SUM(tbl_short_term_programme.wed_a) AS wed_a_total,
SUM(tbl_short_term_programme.thu_p) AS thu_p_total, SUM(tbl_short_term_programme.thu_a) AS thu_a_total,
SUM(tbl_short_term_programme.fri_p) AS fri_p_total, SUM(tbl_short_term_programme.fri_a) AS fri_a_total,
SUM(tbl_short_term_programme.sat_p) AS sat_p_total, SUM(tbl_short_term_programme.sat_a) AS sat_a_total
FROM tbl_short_term_programme
INNER JOIN tbl_phase ON tbl_short_term_programme.phase_id = tbl_phase.phase_id
INNER JOIN tbl_company ON tbl_short_term_programme.company_id = tbl_company.company_id
INNER JOIN tbl_shift ON tbl_short_term_programme.shift_id = tbl_shift.shift_id
CROSS JOIN (SELECT #a := 0) AS var
WHERE tbl_phase.phase_hash = ? AND YEARWEEK(tbl_short_term_programme.short_term_date, 2) = YEARWEEK(?, 2)
GROUP BY tbl_shift.shift_id, tbl_company.comp_name
UNION
SELECT #b := #b + 1 AS sortOne, 2 AS sortTwo, tbl_company.comp_name AS comp_name, tbl_shift.shift_name AS shift_name,
COUNT(tbl_short_term_programme.sun_p) AS sun_p_total, COUNT(tbl_short_term_programme.sun_a) AS sun_a_total,
COUNT(tbl_short_term_programme.mon_p) AS mon_p_total, COUNT(tbl_short_term_programme.mon_a) AS mon_a_total,
COUNT(tbl_short_term_programme.tue_p) AS tue_p_total, COUNT(tbl_short_term_programme.tue_a) AS tue_a_total,
COUNT(tbl_short_term_programme.wed_p) AS wed_p_total, COUNT(tbl_short_term_programme.wed_a) AS wed_a_total,
COUNT(tbl_short_term_programme.thu_p) AS thu_p_total, COUNT(tbl_short_term_programme.thu_a) AS thu_a_total,
COUNT(tbl_short_term_programme.fri_p) AS fri_p_total, COUNT(tbl_short_term_programme.fri_a) AS fri_a_total,
COUNT(tbl_short_term_programme.sat_p) AS sat_p_total, COUNT(tbl_short_term_programme.sat_a) AS sat_a_total
FROM tbl_short_term_programme
INNER JOIN tbl_phase ON tbl_short_term_programme.phase_id = tbl_phase.phase_id
INNER JOIN tbl_company ON tbl_short_term_programme.company_id = tbl_company.company_id
INNER JOIN tbl_shift ON tbl_short_term_programme.shift_id = tbl_shift.shift_id
CROSS JOIN (SELECT #b :- 0) AS var
WHERE tbl_phase.phase_hash = ? AND YEARWEEK(tbl_short_term_programme.short_term_date, 2) = YEARWEEK(?, 2)
GROUP BY tbl_shift.shift_id, tbl_company.comp_name
) AS result ORDER BY sortOne, sortTwo
By default, you can only run one SQL statement per call to mysqli_prepare().
I suggest setting the session variables in a separate statement:
$mysqli->query("set #a = 0, #b = 0");
Then do your prepare & execute of your big query.
Some people might tell you to use the mysqli_multi_query() function, but you can't because it doesn't support prepared statements.
It's okay though, there's no advantage to doing multiple queries in a single call. Just set your variables in one call and then do the prepared statement subsequently. As long as you use the same db connection, the session variables will still have their values.
My code:
$sql = "SET #row_number = 0; SELECT (#row_number:=#row_number + 1) AS num, player, reinforcees, reinforcers FROM _KoT_villages WHERE o = '".$data[2]."' OR o = '".$data[4]."' ORDER BY CASE WHEN player = '".$user_class->id."' THEN 1 ELSE 2 END";
$result = mysql_query($sql) or die(mysql_error());
The error:
You have an error in your SQL syntax; check the manual that
corresponds to your MariaDB server version for the right syntax to use
near 'SELECT (#row_number:=#row_number + 1) AS num, player,
reinforcees, reinforcers F' at line 1
When I echo the SQL statement, I get the following: SET #row_number = 0; SELECT (#row_number:=#row_number + 1) AS num, player, reinforcees, reinforcers FROM _KoT_villages WHERE o = '1' OR o = '5' ORDER BY CASE WHEN player = '2' THEN 1 ELSE 2 END
When I run that echod statement through phpMyAdmin, it runs successfully, and gives me rows of results.
But why doesn't it work in a PHP statement? What's going on? How do I get PHP to do what SQL is doing?
And before anyone says I haven't tried to find the answer myself, if you Google "sql variable" php site:stackoverflow.com, every question I find is about accessing SQL results in PHP (i.e., loops) or inserting PHP variables in SQL, which is not what I need. I'm trying to insert an SQL variable into the SQL statement.
Also: I realize I should stop using MySQL, and am in the process of converting, but in order to quickly resolve a bug...I'm using MySQL.
mysql_query doesn't support multiple queries in one call. You would need to upgrade to mysqli or PDO to enable that. In the meantime though, you can implement what you want in a single query using a CROSS JOIN to initialise the row_number variable e.g.
$sql = "SELECT (#row_number:=#row_number + 1) AS num, player, reinforcees, reinforcers
FROM _KoT_villages
CROSS JOIN (SELECT #row_number := 0) r
WHERE o = '".$data[2]."' OR o = '".$data[4]."'
ORDER BY CASE WHEN player = '".$user_class->id."' THEN 1 ELSE 2 END";
The MySQL client in php expects individual statements
Open MySQL client
First Statement:
SET #row_number = 0
2nd Statement:
SELECT
(#row_number:=#row_number + 1) AS num,
player,
reinforcees,
reinforcers
FROM
_KoT_villages
WHERE
o = '".$data[2]."'
OR o = '".$data[4]."'
ORDER BY
CASE WHEN player = '".$user_class->id."' THEN 1
ELSE 2
END
$result = mysql_query( [2nd Statement] ) or die(mysql_error());
Close MySQL client
I am using the MySQL build-in operators to get a running total, the query works well when I run it in phpMyAdmin, but running it in php does not work, I just get the "Couldn't execute query" message.
My database connection works fine, thus can I do this in php or is there another way to get this to work?
Here is the script:
include("../../include/xxx.inc");
$cxn = mysqli_connect($host,$user,$password,$dbname);
$query = "SET #runtot:=0;
SELECT `q1`.`c`, (#runtot := #runtot + `q1`.`c`) AS rt
FROM (
SELECT SUM( `sr`.`sr_qty` * `st`.`st_ton` ) AS c, `sr`.`sr_no`
FROM `sr`
JOIN `st` ON `sr`.`st_code` = `st`.`st_code`
WHERE `sr`.`sr_date` BETWEEN '2015-01-15' AND '2015-02-15'
GROUP BY `sr`.`sr_no`
ORDER BY `sr`.`sr_no`) AS q1";
$result = mysqli_query($cxn,$query)
or die ("Couldn't execute query.");
while($row = mysqli_fetch_assoc($result))
{
extract($row);
echo "$rt Ton";
Try setting the variable inside the query, so you only have one statement:
SELECT `q1`.`c`, (#runtot := #runtot + `q1`.`c`) AS rt
FROM (
SELECT SUM( `sr`.`sr_qty` * `st`.`st_ton` ) AS c, `sr`.`sr_no`
FROM (SELECT #runtst := 0) vars cross join
`sr`
JOIN `st` ON `sr`.`st_code` = `st`.`st_code`
WHERE `sr`.`sr_date` BETWEEN '2015-01-15' AND '2015-02-15'
GROUP BY `sr`.`sr_no`
ORDER BY `sr`.`sr_no`) AS q1
I'm not completely sure, but i think it's all your `'s
i would do SELECT 'q2.c', (# an so on.
But again, i might be wrong, but it's something to try.
You can't run multiple queries with mysqli_query. You need to call it separately for each query:
$query1 = "SET #runtot:=0"
$query2 = "SELECT `q1`.`c`, (#runtot := #runtot + `q1`.`c`) AS rt
FROM (
SELECT SUM( `sr`.`sr_qty` * `st`.`st_ton` ) AS c, `sr`.`sr_no`
FROM `sr`
JOIN `st` ON `sr`.`st_code` = `st`.`st_code`
WHERE `sr`.`sr_date` BETWEEN '2015-01-15' AND '2015-02-15'
GROUP BY `sr`.`sr_no`
ORDER BY `sr`.`sr_no`) AS q1";
mysqli_query($query1);
$result = mysqli_query($query2);
You can also put variable initializations into a subquery:
$query = "SELECT `q1`.`c`, (#runtot := #runtot + `q1`.`c`) AS rt
FROM (
SELECT SUM( `sr`.`sr_qty` * `st`.`st_ton` ) AS c, `sr`.`sr_no`
FROM `sr`
JOIN `st` ON `sr`.`st_code` = `st`.`st_code`
WHERE `sr`.`sr_date` BETWEEN '2015-01-15' AND '2015-02-15'
GROUP BY `sr`.`sr_no`
ORDER BY `sr`.`sr_no`) AS q1
CROSS JOIN (SELECT #runtot := 0) AS init";
mysqli also has mysqli_multi_query, which allows running multiple queries, but I've seen too many people run into problems to recommend it. It's rarely needed, IMHO.
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.
The following MySQL query runs in PHP without errors, but the resultset is empty. Directly outputting the query string to a file and running the query in the MySQL client using 'source [filename]' returns several rows of results, as expected.
Is there something that would cause this query not to work with PHP? categorylinks.cl_to and smw_spec2.value_string are both varbinary(255). Show create table indicates engine=InnoDB and default charset=binary.
Things I have tried without success:
$sql = preg_replace("/[\n\t]+/", " ", $sql);
Changing '_wpg' and 'Derp' to CAST('_wpg' AS BINARY(255))
Changing '_wpg' and 'Derp' to BINARY '_wpg'
I am using the MediaWiki DatabaseMysql class to execute the query and fetch rows, but it's a very thin abstraction, and I'm certain it's not the problem (see below).
SELECT
prop.name AS prop_name, prop.count AS prop_count, prop.type AS prop_type,
val.value AS val_value, val.unit AS val_unit, val.count AS val_count
FROM
(
SELECT
s_id, name, type, COUNT(foo.name) AS count
FROM (
(
SELECT
cl.cl_to AS cat_name, s.smw_id AS s_id, s.smw_sortkey AS name, spec.value_string AS type
FROM `smw_ids` s
INNER JOIN (`categorylinks` cl, `page` p, `smw_ids` s2, `smw_atts2` a)
ON (cl.cl_from = p.page_id AND
p.page_title = s2.smw_title AND
s2.smw_id = a.s_id AND
a.p_id = s.smw_id)
LEFT JOIN `smw_spec2` spec ON s.smw_id = spec.s_id
)
UNION ALL
(
SELECT
cl.cl_to AS cat_name, s.smw_id AS s_id, s.smw_sortkey AS name, '_wpg' AS type
FROM `smw_ids` s
INNER JOIN (`categorylinks` cl, `page` p, `smw_ids` s2, `smw_rels2` a)
ON (cl.cl_from = p.page_id AND
p.page_title = s2.smw_title AND
s2.smw_id = a.s_id AND
a.p_id = s.smw_id)
)
) AS foo
WHERE foo.cat_name = 'Derp'
GROUP BY name
ORDER BY count DESC
LIMIT 10
) AS prop
INNER JOIN
(
SELECT
bar.p_id AS p_id, bar.value AS value, bar.unit AS unit, COUNT(bar.value) AS count,
IF( #prev != p_id, #rownum := 1, #rownum := #rownum+1 ) AS rank,
#prev := p_id
FROM (
(SELECT a.p_id AS p_id, a.value_xsd AS value, a.value_unit AS unit FROM `smw_atts2` a)
UNION ALL
(SELECT r.p_id AS p_id, s.smw_sortkey AS value, NULL AS unit
FROM `smw_rels2` r INNER JOIN `smw_ids` s ON r.o_id = s.smw_id)
) AS bar
GROUP BY value, unit
ORDER BY count DESC
) AS val
ON prop.s_id = val.p_id
WHERE val.rank <= 50
ORDER BY prop_count DESC, prop_name, val_count DESC, val_value
Edit: The following test script outputs nothing. query.sql contains exactly the query above, written to file immediately preceding the mysql_query() call in MediaWiki's database class.
$db = mysql_connect('localhost', 'root', '');
mysql_select_db('mediawiki', $db);
$res = mysql_query(file_get_contents("query.sql"), $db);
while ($row = mysql_fetch_assoc($res)) {
var_dump($row);
}
echo mysql_error($db);
Edit: I imported a huge database dump and afterwards, when I loaded the PHP page, there was a noticeable wait that seemed to indicate that the query was running, but still no results showed. I ended up reworking the query, and I no longer have this problem.
Try this to detect and report errors better:
$db = mysql_connect('localhost', 'root', '');
mysql_select_db('mediawiki', $db);
$res = mysql_query(file_get_contents("query.sql"), $db);
if (!$res) {
print "SQL Error ".mysql_errno().":".mysql_error().", from query: '".file_get_contents("query.sql")."'";
} else {
while ($row = mysql_fetch_assoc($res)) {
var_dump($row);
}
}
Try this after mysql_connect:
mysql_query('SET NAMES utf8;');