Get columns names when result set (stored procedure) is empty (Laravel) - php

I have come across a situation where the columns generated by my stored procedure are dynamic. I want to know, do we have any method to get the columns names of a result set when the result set returned by stored procedure is empty in Laravel?
I can easily access columns by iterations over the result set when it is not empty but is there any method to access the column names when the data is empty?
That's what I mean to be exact.
$data = Schema::getColumnListing("call conflictReport(123,'2016-08-01 09:00:31','2016-08-01 09:00:31')");
My procedure takes 3 parameters as in parameters.
When the result set is empty, I can't access the column names. I am using getColumnListing of Laravel.

Here's how to do it directly with PDO. With Laravel, you should be able to us the getPdo() function of your MySQLConnection object.
<?php
$pdo = new PDO('mysql:host=localhost;dbname=test', 'root', 'xxxxxxxx');
$stmt = $pdo->query('select * from mytable where false');
$colCount = $stmt->columnCount();
for ($col = 0; $col < $colCount; ++$col) {
print_r($stmt->getColumnMeta($col));
}
I tested and found this works with a CALL query too:
$stmt = $pdo->query('call myproc()');
Basically any statement with a result set has column metadata.
See PDOStatement::getColumnMeta() for more information.

Related

How to loop through a fetched query in PHP using mysql

I have a PHP class where I am initializing an array retrieved from database in the constructor:
class TableClass{
public function __construct($con, $id){
$this->con = $con;
$query = mysqli_query($this->con, "SELECT * FROM table1 WHERE id='$id'");
$this->table_result= mysqli_fetch_array($query);
}
}
I have different functions that I want to use the array without having to fetch again. I just want to loop through the results and make some calculations.
I tried the following syntax:
public function getNumberOfComments(){
for ($i = 0; $i < count($this->table_result); $i++) {
$comment= $this->table_result[$i]['comment'];
}
}
I am getting an error:
Illegal string offset "comment"
What is the correct syntax?
You are fetching one row from the result set:
$this->table_result= mysqli_fetch_array($query);
Assuming that the id is the primary key, you will have 0 or 1 rows in your result set, so if a row is found, you can access the fields directly without using a loop:
$comment= $this->table_result['comment'];
Illegal string offset "comment" is telling you that the value of $this->table_result[$i] is not an array, and therefore does not contain an element named comment.
The problem is possibly being caused by the code in the constructor failing to work properly; you don't have any error checking in there, so if it fails, it won't tell you about it.
Add some error checking in the constructor, find out why it's failing, and fix it.

Php selecting specific columns using WHERE

I am aware this is somehow a noobish question, and I already knkow one option of solving it, but still. I have a database and a search form. What I am trying to accomplish is filter rows specific to what user sends me VIA post. I know how to get POST to my php variables, what I am trying to accomplish now is use variable as criteria only if it is not null. So something in the lines of>
SELECT * FROM db
WHERE COL1=ifnotnull($variable from post form)
AND COL# = ifnotnull($anothervariable)
.
.
.
So I want to include in my search only fileds from form which were entered (i.e. are not null)
I am aware this could be done via preprocessing all variables and checking if they are null and then not using them in SQL query, but is there a shorter way doing this on the fly with SQL?
I hope this I explained it thoroughly,
thanks.
I consider preprocessing all variables as the most preferable approach. But here is tricky solution just for your case. (with mysql user defined variables):
...
$dbh = new PDO(...); // a PDO instance representing a connection to a database
$var1 = 0; // sanitized variable from POST array
$var2 = "pen"; // sanitized variable from POST array
$dbh->exec("SET #var1 := $var1; ");
$dbh->exec("SET #var2 := '$var2'; ");
// replace 'id' and 'name' with your column names
$stmt = $dbh->prepare("SELECT * FROM table_name WHERE (id = #var1 OR #var1 = '') AND (name = #var2 OR #var2 = '') ");
$stmt->execute();
....
Such approach takes into account all passed variables which are not empty

mysqli::query() expects parameter 1 to be string, object given

I had saved the required steps for a parameterized string match query that outputs the subsequent rows. I had lost my files when transferring to a faulty drive. So... I'm trying to mash things together and this isn't working.
$stmt = $link->prepare("SELECT id,entry,date from table WHERE string=? ORDER by Id DESC");
$stmt->bind_param('s',$string);
$stmt->execute();
$stmt->bind_result($id_db,$entry_db,$date_db);
if (($result = $link->query($stmt)) {
while ($row = $result->fetch_row()){
}
}
I can already tell that this is wrong, I don't use the parameterized results and I'm trying to use array indexes such as $row[0].
Going to get yelled at for this one I know.
The end result I want is for example:
string1 has rows: bob, mack, chris
string2 has rows: alice, claire, lara
If $string=string1 then the output should be:
chris
mack
bob
I believe my problem is I am mixing statement types
Assuming that "$link" is an instance of PHP's "mysqli" class, and that "id" and "Id" are two diffrent columns in your table (if it's not the case, please try replacing "Id" with "id" in the segment ".. ORDER BY Id.."), here is, based on your example, what I suggest that you try:
// Declare your "prepare" statement (make sure to change "Id" for "id" if both are used
// in reference to the same column in your table)
$stmt = $link->prepare('SELECT id, entry, date FROM table WHERE string = ? ORDER BY Id DESC');
// Bind the $string variable
$stmt->bind_param('s',$string);
// Execute the statement
$stmt->execute();
// Store the result (this is sometimes useful, depending on the data types in your table)
$stmt->store_result();
// Check whether at least one row in table matches the query, if not, stop here...
if ($stmt->num_rows === 0) exit('No matching rows'); // or do something else...
// Declare a container (i.e. storage) for each row (this is optional and depends on what
// you are trying to achieve)
$data = [];
// Loop through results (this is just an example; this could be achieved in a number
// of different ways)
for ($i = 0; $i < $stmt->num_rows; $i++)
{
// Add a new array cell to $data, at index $i
$data[$i] = [];
// Bind result for row $i
$stmt->bind_result($data[$i]['id'],$data[$i]['entry'],$data[$i]['date']);
// Fetch $i^{th} row
$stmt->fetch();
}
// Check if it worked (temporary)
var_dump($data);

How to get "field names" using PHP ADOdb?

I'm using PHP ADOdb and I can get the result set:
$result = &$db->Execute($query);
How do I get the field names from that one row and loop through it?
(I'm using access database if that matters.)
It will depend on your fetch mode - if you setFetchMode to ADODB_FETCH_NUM (probably the default) each row contains a flat array of columns. If you setFetchMode to ADODB_FETCH_ASSOC you get an associative array where you can access each value by a key. The following is taken from ADODB documentation - http://phplens.com/lens/adodb/docs-adodb.htm#ex1
$db->SetFetchMode(ADODB_FETCH_NUM);
$rs1 = $db->Execute('select * from table');
$db->SetFetchMode(ADODB_FETCH_ASSOC);
$rs2 = $db->Execute('select * from table');
print_r($rs1->fields); # shows array([0]=>'v0',[1] =>'v1')
print_r($rs2->fields); # shows array(['col1']=>'v0',['col2'] =>'v1')
To loop through a set of results:
$result = &$db->Execute($query);
foreach ($result as $row) {
print_r($row);
}
Small improvement to the solution posted by #thetaiko.
If you are ONLY needing the field names, append LIMIT 1 to the end of your select statement (as shown below). This will tell the server to send you a single row with column names, rather than sending you the entire table.
SELECT * FROM table LIMIT 1;
I'm working with a table that contains 9.1M records, so this minor change speeds up the query significantly!
This is a function I use to return a field array - I've stripped out some extra stuff that, for example, allows it to work with other DBs than MySQL.
function getFieldNames($strTable, $cn) {
$aRet = array();
# Get Field Names:
$lngCountFields = 0;
$strSQL = "SELECT * FROM $strTable LIMIT 1;";
$rs = $cn->Execute($strSQL)
or die("Error in query: \n$strSQL\n" . $cn->ErrorMsg());
if (!$rs->EOF) {
for ($i = 0; $i < $rs->FieldCount(); $i++) {
$fld = $rs->FetchField($i);
$aRet[$lngCountFields] = $fld->name;
$lngCountFields++;
}
}
$rs->Close();
$rs = null;
return $aRet;
}
Edit: just to point out that, as I say, I've stripped out some extra stuff, and the EOF check is therefore no longer necessary in the above, reduced version.
I initally tried to use MetaColumnNames, but it gave differing results in VisualPHPUnit and actual site, while running from the same server, so eventually
I ended up doing something like this:
$sql = "select column_name, column_key, column_default, data_type, table_name, table_schema from information_schema.columns";
$sql .= ' where table_name="'.$table.'" and table_schema="'.$database_name.'"';
$result = $conn->Execute($sql);
while($row = $result->fetchRow()) {
$out[] = strToUpper($row['column_name']);
}
I think it should work with mysql, mssql and postgres.
The benefit of doing it like this, is that you can get the column names, even if a query from a table returns an empty set.
If you need the Coloumn names even for empty tables or for joins about multiple tables use this:
$db->Execute("SELECT .......");
// FieldTypesArray - Reads ColoumnInfo from Result, even for Joins
$colInfo = $res->FieldTypesArray();
$colNames = array();
foreach($colInfo as $info) $colNames[] = $info->name;
The OP is asking for a list of fieldnames that would result of executing an sql statement stored in $query.
Using $result->fetchRow(), even with fetch mode set to associative, will return nothing if no records match the criteria set by $query. The $result->fields array would also be empty and would give no information for getting the fieldnames list.
Actually, we don't know what's inside the $query statement. Besides, setting limit to 1 may not compatible with all database drivers supported by PHP ADOdb.
Answer by Radon8472 is the right one, but the correct code could be:
$result = $db->Execute($query);
// FieldTypesArray - an array of ADOFieldObject Objects
// read from $result, even for empty sets or when
// using * as field list.
$colInfo = [];
if (is_subclass_of($result, 'ADORecordSet')){
foreach ($result->FieldTypesArray() as $info) {
$colInfo[] = $info->name;
}
}
I have the habit of checking the class name of $result, for as PHP ADOdb will return false if execution fails.

how to identify the source table of fields from a mysql query

I have two dynamic tables (tabx and taby) which are created and maintained through a php interface where columns can be added, deleted, renamed etc.
I want to read all columns simulataneously from the two tables like so;-
select * from tabx,taby where ... ;
I want to be able to tell from the result of the query whether each column came from either tabx or taby - is there a way to force mysql to return fully qualified column names e.g. tabx.col1, tabx.col2, taby.coln etc?
In PHP, you can get the field information from the result, like so (stolen from a project I wrote long ago):
/*
Similar to mysql_fetch_assoc(), this function returns an associative array
given a mysql resource, but prepends the table name (or table alias, if
used in the query) to the column name, effectively namespacing the column
names and allowing SELECTS for column names that would otherwise have collided
when building a row's associative array.
*/
function mysql_fetch_assoc_with_table_names($resource) {
// get a numerically indexed row, which includes all fields, even if their names collide
$row = mysql_fetch_row($resource);
if( ! $row)
return $row;
$result = array();
$size = count($row);
for($i = 0; $i < $size; $i++) {
// now fetch the field information
$info = mysql_fetch_field($resource, $i);
$table = $info->table;
$name = $info->name;
// and make an associative array, where the key is $table.$name
$result["$table.$name"] = $row[$i]; // e.g. $result["user.name"] = "Joe Schmoe";
}
return $result;
}
Then you can use it like this:
$resource = mysql_query("SELECT * FROM user JOIN question USING (user_id)");
while($row = mysql_fetch_assoc_with_table_names($resource)) {
echo $row['question.title'] . ' Asked by ' . $row['user.name'] . "\n";
}
So to answer your question directly, the table name data is always sent by MySQL -- It's up to the client to tell you where each column came from. If you really want MySQL to return each column name unambiguously, you will need to modify your queries to do the aliasing explicitly, like #Shabbyrobe suggested.
select * from tabx tx, taby ty where ... ;
Does:
SELECT tabx.*, taby.* FROM tabx, taby WHERE ...
work?
I'm left wondering what you are trying to accomplish. First of all, adding and removing columns from a table is a strange practice; it implies that the schema of your data is changing at run-time.
Furthermore, to query from the two tables at the same time, there should be some kind of relationship between them. Rows in one table should be correlated in some way with rows of the other table. If this is not the case, you're better off doing two separate SELECT queries.
The answer to your question has already been given: SELECT tablename.* to retrieve all the columns from the given table. This may or may not work correctly if there are columns with the same name in both tables; you should look that up in the documentation.
Could you give us more information on the problem you're trying to solve? I think there's a good chance you're going about this the wrong way.
Leaving aside any questions about why you might want to do this, and why you would want to do a cross join here at all, here's the best way I can come up with off the top of my head.
You could try doing an EXPLAIN on each table and build the select statement programatically from the result. Here's a poor example of a script which will give you a dynamically generated field list with aliases. This will increase the number of queries you perform though as each table in the dynamically generated query will cause an EXPLAIN query to be fired (although this could be mitigated with caching fairly easily).
<?php
$pdo = new PDO($dsn, $user, $pass, array(PDO::ATTR_ERRMODE=>PDO::ERRMODE_EXCEPTION));
function aliasFields($pdo, $table, $delim='__') {
$fields = array();
// gotta sanitise the table name - can't do it with prepared statement
$table = preg_replace('/[^A-z0-9_]/', "", $table);
foreach ($pdo->query("EXPLAIN `".$table."`") as $row) {
$fields[] = $table.'.'.$row['Field'].' as '.$table.$delim.$row['Field'];
}
return $fields;
}
$fieldAliases = array_merge(aliasFields($pdo, 'artist'), aliasFields($pdo, 'event'));
$query = 'SELECT '.implode(', ', $fieldAliases).' FROM artist, event';
echo $query;
The result is a query that looks like this, with the table and column name separated by two underscores (or whatever delimeter you like, see the third parameter to aliasFields()):
// ABOVE PROGRAM'S OUTPUT (assuming database exists)
SELECT artist__artist_id, artist__event_id, artist__artist_name, event__event_id, event__event_name FROM artist, event
From there, when you iterate over the results, you can just do an explode on each field name with the same delimeter to get the table name and field name.
John Douthat's answer is much better than the above. It would only be useful if the field metadata was not returned by the database, as PDO threatens may be the case with some drivers.
Here is a simple snippet for how to do what John suggetsted using PDO instead of mysql_*():
<?php
$pdo = new PDO($dsn, $user, $pass, array(PDO::ATTR_ERRMODE=>PDO::ERRMODE_EXCEPTION));
$query = 'SELECT artist.*, eventartist.* FROM artist, eventartist LIMIT 1';
$stmt = $pdo->prepare($query);
$stmt->execute();
while ($row = $stmt->fetch()) {
foreach ($row as $key=>$value) {
if (is_int($key)) {
$meta = $stmt->getColumnMeta($key);
echo $meta['table'].".".$meta['name']."<br />";
}
}
}

Categories