I'm getting a weird result from a quite long query, which I will simplify here:
DROP TEMPORARY TABLE IF EXISTS table1;
CREATE TEMPORARY TABLE table1 AS
(SELECT
parent.id as parent_id,
times.a_time,
times.sequence,
FROM times
LEFT JOIN parent ON times.parent_id=parent.id
WHERE times.stop_id=10);
DROP TEMPORARY TABLE IF EXISTS table2;
CREATE TEMPORARY TABLE table2 AS
(SELECT
parent.id as parent_id,
times.b_time,
times.sequence,
FROM times
LEFT JOIN parent ON times.parent_id=parent.id
WHERE times.stop_id=15 );
--here comes PDO->exec();
SELECT table1.*, table2.b_time
FROM table1
LEFT JOIN table2 ON table1.parent_id=table2.parent_id
WHERE table2.parent_id IS NOT NULL AND table1.sequence<table2.sequence
ORDER BY table1.a_time
I'm testing the query using EMS MySQL Manager 2007, and in PHP I'm using PDO query.
In order to get the final result, (I know that PDO doesn't support running this full query at once and giving back the result set), I run PDO->exec() after temporary tables creation (see comment in the query), and then I run PDO->query() on the last SELECT:
$db = new PDO("mysql:host=".DB_HOST.";dbname=".DB_NAME, DB_USER, DB_PASS);
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$db->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC);
$tempTablesSQL='DROP TEMPORARY TABLE IF EXISTS...'; //create temporary tables
$db->exec($tempTablesSQL);
$sql='SELECT table1.*, table2.b_time ...'; //JOIN and SELECT the results
$results=array();
foreach($db->query($sql) as $row){
$results[]=$row;
}
print_r($results);
In MySQL Manager I run the whole query at once, and for those specific IDs I'm getting 29 rows as result (which is correct, because the records are inserted from a previously parsed file, and by comparing the results to the file I know they are good).
But in PHP, I'm getting only 25 results, and totally wrong values for b_time.
So, my questions are:
why do I get wrong results?
is my approach of calling this query wrong (in PHP)?
Any help is appreciated.
--EDIT--
It's not just PDO, I tried with mysqli_multi_query, I'm getting the same wrong results.
One important thing I noticed is: if I use regular tables instead of the temporary, the results are fine.
Let my try to put a couple of suggestions on the second part of your question which is is my approach of calling this query wrong?
First of all it looks like you don't need to create any temp tables. Based on what you showed the whole thing can be a single query like
SELECT q1.parent_id, q1.a_time, q1.sequence, q2.b_time
FROM
(
SELECT p.id parent_id, t.a_time, t.sequence
FROM times t LEFT JOIN parent p
ON t.parent_id=p.id
WHERE t.stop_id = ?
) q1 LEFT JOIN
(
SELECT p.id as parent_id, t.b_time, t.sequence
FROM times t LEFT JOIN parent p
ON t.parent_id=parent.id
WHERE t.stop_id = ?
) q2 ON q2.parent_id IS NOT NULL
AND q1.sequence < q2.sequence
ORDER BY q1.a_time
And execute it as a prepared statement
...
$sql = 'SELECT ...'; // the whole thing from above
$query = $db->prepare($sql);
$query->execute(array($stop_id1, $stop_id2));
$result = $query->fetchAll(PDO::FETCH_ASSOC);
var_dump($result);
Now even if for some reason you have to use temp tables and perform some manipulations along the way before returning the result set then I'd suggest to wrap it up a stored procedure
DELIMITER $$
CREATE PROCEDURE sp_myproc (IN stop_id1 INT, IN stop_id2 INT, ...)
BEGIN
DROP TEMPORARY TABLE IF EXISTS table1;
CREATE TEMPORARY TABLE table1 AS
...
WHERE times.stop_id = stop_id1;
DROP TEMPORARY TABLE IF EXISTS table1;
CREATE TEMPORARY TABLE table1 AS
...
WHERE times.stop_id = stop_id2
-- return the resultset
SELECT table1.*, table2.b_time
FROM table1
...
END$$
DELIMITER ;
And call your procedure once from php
...
$sql = 'CALL sp_myproc(?, ?)';
$query = $db->prepare($sql);
$query->execute(array($stop_id1, $stop_id2));
$result = $query->fetchAll(PDO::FETCH_ASSOC);
var_dump($result);
Now regarding the first part of your question why do I get wrong results? OUTER joins can be tricky and especially when you chain them. You can easily filter some rows out or produce additional rows (which happens most often).
Those few joins that you removed may be the cause.
Anyway provided information is not enough for conclusive answer.
But I would suggest instead of returning columns as table1.* specify all columns explicitly and give explicit aliases for columns that have the same names in different tables that are part of a join.
Related
I have two different databases and I have to run a query where I am going to join tables from both databases in a single query. My query goes like this:
SELECT mm.* FROM tbl1 mm INNER JOIN tbl2 t ON mm.module_id=t.module_id WHERE mm.module_name NOT IN(SELECT module_name FROM tbl3)"
Here tbl1 and tbl2 are in one database and tbl3 is in the second database. Is it possible to run this query without HARD CODING the database names before the table names? I created common files global_config1.php and global_config2.php which have the database constants and other php database connection constants and am using require_once(global_config1.php) and require_once(global_config2.php) but the query fails.
Try this:
SELECT Database1.TableA.ColumnA1, Database1.TableA.ColumnA2,
Database2.TableB.ColumnB1, Database2.TableB.ColumnB2
FROM Database1.TableA
INNER JOIN
Database2.TableB ON
Database1.TableA.ColumnA3 = Database2.TableB.ColumnB3
Now, you will see the complete result in a single row. In case, the column names are same in both databases, you will see the column names same in the result and it will be impossible to get the value in PHP because 2 or more column name is same. To accomplish this, lets write another query
you can use two different database to fetch a record, for this you have two database object.
try this,
<?php
$con = mysqli_connect("localhost","root","","test");
$con1 = mysqli_connect("localhost","root","","test1");
$query = "SELECT mm.* FROM test.tbl1 mm INNER JOIN test.tbl2 t ON mm.module_id=t.module_id WHERE mm.module_name NOT IN(SELECT module_name FROM test1.tbl3)"
?>
For CI
$query = $this ->db->query("SELECT * FROM Table-1 WHERE id = (SELECT qid FROM Table-2 where post_status ='active' AND post_type= 'quick') ");
return $query->result_array();
My purpose was resolved by separating the queries and the database connection string variables. So to run a query on one database, I entered separate connection string variables in mysql_query(). This helped fetch the data from both databases.
I am trying to create a temporary table from the results of multiple tables that are alike (same table structure). After creating the temporary table and runing the subsequent queries, I would like to store the results from the temporary table in an array to be accessible later in the script/program. I have tried searching for an answer and can't seem to find one.
I have tried nextRowset() as well as separating the queries, but nothing seems to be working like I expect it to work.
Here is my code:
$pdo = new PDO("mysql:host=".$_SESSION['server'].";dbname=data".$_SESSION['sysident'],$user,$pass);
$stmt = $pdo->prepare("DROP TABLE IF EXISTS $tabletocreate;
CREATE TEMPORARY TABLE $tabletocreate LIKE table1;
INSERT INTO $tabletocreate (SELECT * FROM table1 WHERE (MISC LIKE '%:memno%' OR MEMNO = :memno)) UNION (SELECT * FROM table2 WHERE (MISC LIKE '%:memno%' OR MEMNO = :memno)) UNION (SELECT * FROM table3 WHERE (MISC LIKE '%:memno%' OR MEMNO = :memno)) ORDER BY SLIPNO;
SELECT * FROM $tabletocreate");
$stmt->bindParam(":memno",$_SESSION['memno']);
$stmt->execute();
$stmt->nextRowset();
$test = $stmt->fetchAll();
print_r($test);
I am unsure as to why the results are not being stored into the array. From what I can tell, everything seems right and no errors occur when the script is ran. I appreciate any help that anyone can offer.
UPDATE - I found out why the query wasn't working. I was using a "-" in the table name I was trying to create which isn't allowed.
You cannot run multiple queries in a single ->query() call. This is a security measure in the underlying PHP mysql drivers, to prevent some form of SQL injection attacks. Doesn't matter which DB interface library you're using, because they all use the same underlying drivers. You'll have to run each seperate query separately:
->query("DROP TABLE IF EXISTS ...");
->query("CREATE TEMPORARY TABLE ...");
->query("INSERT INTO ...");
etc...
I was trying to create a table name with a "-" in the table name. After removing this from the table name, all the queries executed successfully and my PHP code worked as intended.
I am trying to write a dual MySQL query which archives the content of a table to another table, then deletes the original row in the original table.
I have the base query working just fine in other areas. The query below relies on a second table to help select which rows need to be archived & deleted.
I am getting a syntax error where the AS appears in the nested select of the first query and near the AS in the delete query.
I have researched and researched and tried a bunch of different code combos, but I can't get the queries working. The queries are written in PHP using PDO, so please ignore the PDO tags, they are not the problem.
INSERT INTO usetwca (r_id, c_id, o_id, t_id, s_id, ip_address, timestamp, timestamp_archived) SELECT :r_id, usetwc.c_id, usetwc.o_id, usetwc.t_id, usetwc.s_id, usetwc.ip_address, usetwc.timestamp, :timestamp FROM usetwc JOIN useo ON useo.o_id = usetwc.o_id AND useo.best_fit IS NULL WHERE usetwc.user_id = :user_id;
DELETE FROM usetwc JOIN useo ON useo.o_id = usetwc.o_id AND useo.best_fit IS NULL WHERE usetwc.user_id = :user_id;
You should implement this as a BEFORE DELETE trigger on the `users_sessions_exercise_t_ws_correlation' table.
Have a look at this answer as a good example.
Here's a good general reference on using Triggers for Logging.
I was able to get my queries working.
INSERT INTO usetwca (r_id, c_id, o_id, t_id, s_id, ip_address, timestamp, timestamp_archived) SELECT :r_id, usetwc.c_id, usetwc.o_id, usetwc.t_id, usetwc.s_id, usetwc.ip_address, usetwc.timestamp, :timestamp FROM usetwc JOIN useo ON useo.o_id = usetwc.o_id AND useo.best_fit IS NULL WHERE usetwc.user_id = :user_id;
DELETE usetwc FROM usetwc INNER JOIN useo ON useo.o_id = usetwc.o_id AND useo.best_fit IS NULL WHERE usetwc.user_id = :user_id
I want to do a SELECT on an empty table, but i still want to get a single record back with all the column names. I know there are other ways to get the column names from a table, but i want to know if it's possible with some sort of SELECT query.
I know this one works when i run it directly in MySQL:
SELECT * FROM cf_pagetree_elements WHERE 1=0;
But i'm using PHP + PDO (FETCH_CLASS). This just gives me an empty object back instead of an row with all the column names (with empty values). So for some reason that query doesn't work with PDO FETCH_CLASS.
$stmt = $this->db->prepare ( $sql );
$stmt->execute ( $bindings );
$result = $stmt->fetchAll ( \PDO::FETCH_CLASS, $class );
print_r($result); // Empty object... I need an object with column names
Anyone any idea if there's another method that i can try?
Adding on to what w00 answered, there's a solution that doesn't even need a dummy table
SELECT tbl.*
FROM (SELECT 1) AS ignore_me
LEFT JOIN your_table AS tbl
ON 1 = 1
LIMIT 1
In MySQL you can change WHERE 1 = 1 to just WHERE 1
To the other answers who posted about SHOW COLUMNS and the information scheme.
The OP clearly said: "I know there are other ways to get the column names from a table, but i want to know if it's possible with some sort of SELECT query."
Learn to read.
Anyway, to answer your question; No you can't. You cannot select a row from an empty table. Not even a row with empty values, from an empty table.
There is however a trick you can apply to do this.
Create an additional table called 'dummy' with just one column and one row in it:
Table: dummy
dummy_id: 1
That's all. Now you can do a select statement like this:
SELECT * FROM dummy LEFT OUTER JOIN your_table ON 1=1
This will always return one row. It does however contain the 'dummy_id' column too. You can however just ignore that ofcourse and do with the (empty) data what ever you like.
So again, this is just a trick to do it with a SELECT statement. There's no default way to get this done.
SHOW COLUMNS FROM cf_pagetree_elements;
This will give a result set explaining the table structure. You can quite easily parse the result with PHP.
Another method is to query the infomrmation schema table:
SELECT column_name FROM information_schema.columns WHERE table_name='cf_pagetree_elements';
Not really recommended though!
You could try:
SELECT * FROM information_schema.columns
WHERE table_name = "cf_pagetree_elements"
Not sure about your specific PHP+PDO approach (there may be complications), but that's the standard way to fetch column headings (field names).
this will list the columns of ANY query for PDO drivers that support getColumMeta. I am using this with SQL server and works fine even on very complex queries with aliased tables, sub-queries and unions. Gives me columns even when results are zero
<?php
// just an example of an empty query.
$query =$PDOdb->query("SELECT * from something where 1=0; ");
for ($i=0; $i<$query->columnCount(); $i++) {
echo $query->getColumnMeta($i)['name']."<br />";
}
?>
Even without PDO in the way, the database won't return the structure without at least one row. You could do this and ignore the data row:
SELECT * FROM cf_pagetree_elements LIMIT 1;
Or you could simply
DESC cf_pagetree_elements;
and deal with one row per field.
WHERE 1=0 does not work for me. It always returns empty set.
The latest PDO for SQLSVR definitely works with get column meta.
Simply set up your statement and use this to get an array of useful information:
$stmt->execute();
$meta= array();
foreach(range(0, $stmt->columnCount() - 1) as $column_index)
{
array_push($meta,$stmt->getColumnMeta($column_index));
}
Complete solution for Oracle or MySQL
for any or some columns (my goal is to get arbitrary columns exactly as they are in DB regardless of case)
for any table (w or w/o rows)
$qr = <<<SQL
SELECT $cols
FROM (SELECT NULL FROM DUAL)
LEFT JOIN $able t ON 1 = 0
SQL;
$columns = array_keys($con->query($qr)->fetchAll(PDO::FETCH_ASSOC)[0]);
if($cols === "*") {
array_shift($columns);
}
YOu could use MetaData with;
$cols = mysql_query("SHOW COLUMNS FROM $tableName", $conn);
I am joining two tables: customers and queries.
I am getting the full_name from the customers table and the description from the queries table.
I am wondering if it is possible to have the results of an SQL join split into arrays that correspond with the table the data came from? For example:
$STH = $DBH->prepare("SELECT queries.description, customers.full_name FROM queries INNER JOIN customers ON queries.customer_id = customers.id");
$STH->execute();
$queries = $STH->fetchAll();
At the moment, I can access my data like this: $queries[0]['description'] and $queries[0]['full_name']
However, my question is whether there is an easy way to get the data like so: $job[0]['query']['description'] and $job[0]['customer']['full_name'].
Just as teresko mentioned, I can't understand why you'd need that.
I can only imagine you want to see on the PHP code what are the table that contained the information.
Maybe you could do something like SELECT queries.description as queries_description, then your php code would look like $queries['queries_description']. Would it be enough?
You can loop through the results in PHP and convert it to the data structure you want, but you cannot (as far as I know) automatically group the data into arrays based on the source table. A (somewhat messy) alternative, using SQL is to use a multi-query and create a temp table from your original results, then select the results on a per-table basis, like so:
CREATE TEMPORARY TABLE q AS
(SELECT queries.description, customers.full_name FROM queries
INNER JOIN customers
ON queries.customer_id = customers.id
);
SELECT q.description FROM q;
SELECT q.full_name FROM q;
So, in those SELECT statements, you'll have to list all the columns that you want for each result. Then in PHP, you'll have to iterate over each resultset and put the data into arrays (or objects/whatever) as needed. Errr. A fetchAll will still not get you what you want, but a fetchAll on the first non-empty resultset will get you all the rows from queries and the 2nd will get you all the rows from customers