Sql select including columntype - php

I want, for my form in php, a result including columntype.
Is there a way to make a query and including the column type?
select * from TableA
+
SELECT COLUMN_NAME, DATA_TYPE FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = 'yourTableName'

There's probably no way to select both data and column type because the column type is typically stored in a separate table (this would depend on the database). For example, in MySql:
Query to column type:
SELECT COLUMN_NAME,DATA_TYPE
FROM INFORMATION_SCHEMA.COLUMNS
WHERE
TABLE_NAME = 'TableA'
You would then to need to parse the results of the 2 queries and join them using php (I don't think there's an easy way to do this using SQL because you would be joining a 1xN results with a Nx1 result).
More info about where the column type is stored:
http://dev.mysql.com/doc/refman/5.7/en/information-schema.html

Those are inherently different data sets so I bet you'd need to write a possibly rather messy stored routine.
You don't explain why you need it but I suspect you'll be fine with the column types of the result set (rather than the source tables). That will include exactly the same information plus the type of calculated columns.
The exact mechanism depends on your DBMS and database library. You don't give any clue about that but your sample code looks like MySQL so the options include:
PDOStatement::getColumnMeta()
mysqli_stmt::result_metadata()
If you want to stick to your original spec, at least save yourself some trouble and just run two queries.

I found this. So now i can combine the array!
$i = 0;
while ($column = $query->getColumnMeta($i++)) {
print_r($column);
}

Related

PHP - mysqli_fetch_field equivalent for PostgreSQL

I'm trying to move to my project to PostgreSQL from MySQL. For MySQL, PHP offers good functionality to fetch SELECT query metadata.
mysqli_fetch_field or mysqli_fetch_field_direct functions can give the following values for each field in the query
name
orgname
table
orgtable
max_length
length
type
(and a few others I'm not interested in currently)
For instance, an example query and metadata for the field "id" frm the query will look like this:
Query:
SELECT id as id2 FROM table1 t1
Metadata for field "id" :
name: id2
orgname : id
table : t1
orgtable : table1
length: 765
type: 253
The good thing about mysqli_fetch_field is that it is capable of returning both alias and original name of the fields and tables in the query.
When I try to imitate this functionality for PostgreSQL, I observe that there is no equivalent of mysqli_fetch_field function for PostgreSQL, instead, there are several pg_field_* functions each of which give a single information for a field.
pg_field_name returns name only and for this example it gives "id2"
pg_field_table returns original table name, e.g. "table1"
pg_field_type returns the type (no problem here)
pg_field_size returns the length of the field (it is also OK)
Obviously, pg_field_* functions do not offer the programmer original names of the tables/fields if they are renamed in the query while mysqli_fetch_field does.
Is there a way that I can fetch "detailed" metadata for a query in PostgreSQL? It can be using an additional query or a tool that the DBMS offers like EXPLAIN and DESCRIBE (I tried these too) or anything else.
Are you invested into the Postgres specific function API? If not, check out PDO, which provides you one common interface to your database, no matter if it is MySQL or Postgres. It also provides you a way to fetch specific columns:
$pdo = new PDO(...);
$statement = $pdo->prepare("SELECT foo FROM bar WHERE baz = ?");
$statement->execute([42]);
$foo = $statement->fetchColumn(0);
// $foo is the column value or false
You can also fetch column values of all result rows:
$foos = $statement->fetchAll(\PDO::FETCH_COLUMN, 0);
// $foos is an array, possibly empty
Note that fetchAll incurs the usual memory penalty, as a possibly large result is loaded into your process. You should only use fetchAll if the result set is known to always be small. Otherwise, use fetch or fetchColumn in a loop and page the query with LIMIT n, m clauses.
Fetching a specific column is resource friendly if only one column is queried. When you have other columns in your query, they would still be fetched into your process, just to be thrown away. You should make sure to not include any unnecessary columns.
For column metadata, use getColumnMeta:
$meta = $statement->getColumnMeta(0);
// $meta is false or array{native_type, driver:decl_type, flags, name, table, len, precision, pdo_type}
To get more detailed metadata, you should query into information_schema.columns. Best to do it before the data query, because the data query might be executed in a loop. Metadata need to be fetched only once beforehand.
After some research, I am eventually convinced that neither PHP Postgres API nor PostreSQL itself provides such functionality. Therefore, I could manage to solve the problem by a combination of pg_field_* functions and an SQL Parser written in PHP, namely PHP-SQL-Parser
Even though the SQL parser is written MySQL in mind, the default functionality worked for my use case and it is extensible in any case.

Select column(s) names based on user entry with a MYSQL query

Using PHP a secure user will enter a Ref (ex. NB093019) a query will be used to determine which PO(s) have that Ref and if they have any quantity. The issue is that we have 86 columns to check if that Ref is in and then once it finds what column it is in how to check the corresponding column that contains that quantity( the table cannot be edited).
I can make this work with 86 if else statements in PHP and then more if else statements inside of each PHP statement. I have no launching point once i do the initial query.
select 'remainder'as prefix, po, *comments,*GuideRef, *Qty
from remainder
where ('NB092419')IN (NWANTcomments,NWANTGuideRef,NWANTpreviouscomments,
NWANTpreviousGuideRef,NWANTprevious2comments,
NWANTprevious2GuideRef, BPrev2GuideRef,
BPrev2comments, BPrevGuideRef, BPrevcomments,
aGuideRef, Mcomments,MGuideRef,acomments,
MAGuideRef,BOGuideRef )
group by po
I have removed some of the in() information so it is not so long also the *comments, *GuideRef, *Qty would be decided by which one of the columns in the IN() statement returns information. Is this even possible
You could perhaps write an SQL that writes an SQL:
select REPLACE(
'SELECT ''{colstub}GuideRef'' as which, {colstub}Qty FROM remainder WHERE {colstub}Ref like ''%somevalue%'' UNION ALL',
'{colstub}',
REPLACE(column_name, 'GuideRef', '')
)
FROM information_schema.columns
WHERE table_name = 'remainder' and column_name LIKE '%Ref'
It works like "pull all the column names out of the info schema where the column name is like %guideref, replace guideref with nothing to get just the fragment of the column name that is varied: NWANTguideref -> NWANT, NWANTpreviousguideref -> NWANTprevious ... then uses this stub to form a query that gives a string depicting the column name, the qty from the quantity column, where the relevant guideref column is LIKE some value"
If you run this it will produce a result set like:
SELECT 'aGuideRef' as which, aQty FROM table WHERE aGuideRef LIKE '%lookingfor%' UNION ALL
SELECT 'bGuideRef' as which, bQty FROM table WHERE bGuideRef LIKE '%lookingfor% ...
So it's basically utputted a load of strings that are SQLs in themselves. It might need a bit of fine tuning, and hopefully all your columns are reliably and rigidly like xQty, xGuideRef, xComments triplets, but it essentially writes most the query for you
If you then copy the result set out of the results grid and paste it back into the query window, remove the last UNION ALL and run it, it will search the columns and tell you where it was found as well as the quantity
It's not too usable for a production system, but you could do the same in php- run the query, get the strings into another sql command, re-run it..
I would suggest you consider changing your table structure though:
prefix, qty, guideref, comments
You shouldn't have 86 columns that are the mostly same thing; you should have one column that is one of 86/3 different values then you can just query the guideref and the type. If this were an address table, I'm saying you **shouldn't* have HomeZipcode, WorkZipcode, UniversityZipcode, MomZipcode, DadZipcode.. and every time you want to store another kind of address you add more columns (BoyfriendZipcode, GirlfriendZipcode, Child1Zipcode...). Instead if you just had an "addresstype" column then you can store any number of different kinds of addresses without recompiling your app and changing your db schema
You can use this technique to re-shape the table - write an SQL that writes a bunch of UNION ALL sqls (without WHERE clauses), one of the columns should be the "recordtype" column (from colstub) and the other columns should just be "qty", "guide", "comments". Once you have your result set with the unions you can make a table to hold these 4 things, and then place INSERT INTO newtable at the head of the block of unions

Is it better to handle this Query on MySQL side or PHP side?

Explanation:
Writing an extension for a closed-source PHP Program,
I need to add some numbers based on these example Queries
SELECT sum(value) AS sum1 FROM table WHERE user_id=X AND text='TEXT_HERE_A'
SELECT sum(value) AS sum2 FROM table WHERE user_id=X AND text='TEXT_HERE_B'
Add these numbers in PHP and then Update a field in database
$update = $sum1 + $sum2 - $php_sum;
UPDATE table SET value=$update WHERE user_id=X
Question:
As you can see I'm searching based on "text" data type in MySql,
Do you think this action is okay? or should I do the following instead:
SELECT value,text FROM table WHERE user_id=X
and then do the sum and calculations in PHP side through loops (the difference here is I select based on user_id KEY(INT) only, and the sums are calculated are PHP side)
Which one has better performance in large tables?
Question: Which one is better in this situation? Calculating SUMs in PHP side or MySQL side?
It's usually wrong to return lots of rows and do the filtering and summing in the client -- you generally want to minimize the amount of data transferred between the client and server. So if you can do the filtering with a WHERE clause and aggregation with things like SUM and COUNT, it's usually preferable.
I would try to do the whole thing on the server.
UPDATE table AS t1
JOIN (SELECT SUM(value) AS total_value
FROM table
WHERE user_id = X AND text in ('TEXT_HERE_A', 'TEXT_HERE_B')) AS t2
SET value = total_value - $php_sum
WHERE user_id = X
If you do not need the values on your PHP-side code, then simply do the entire operation in one query.
Since now you have multiple queries in your example, your code is prone to concurrency anomalies - interleaved execution will leave your final value at the UPDATE statement inconsistent.
Since your question also asked about TEXT type query, it really depends on what sort of data you are putting into the TEXT column. If you can use a CHAR or VARCHAR instead and put an index on that column, the query would be faster. TEXT unindexed search would take rather long, and it is expensive to index a TEXT column.

Selecting all columns that start with XXX using a wildcard?

I have several columns in my databases with similar names.
How do I select those based on the word they start with?
Here's an example table layout:
I tried selecting all info for a particular thing (food kind in this example) using
$Food = "Vegetable";
mysql_query("SELECT `" . $Food . " %` FROM `Foods`");
but it didn't seem to work.
Any help would be appreciated :-)
EDIT: Apparently this wasn't clear from my example, but I already know all column's first words. The columns are always the same and no 'food kinds' are ever added or deleted. The PHP variable is only there to determine which one of a couple of set kinds I need.
You'd have to build the SQL dynamically. As a starting point, this would get you the column names you're seeking.
SELECT COLUMN_NAME
FROM INFORMATION_SCHEMA.COLUMNS
WHERE table_name = 'Foods'
AND table_schema = 'YourDB'
AND column_name LIKE 'Vegetable%'
There's no way to do exactly what you're trying to. You could do another query first to fetch all the column names, then process them in PHP and build the second query, but that's probably more complex than just writing out the names that you want.
Or is there a reason this query needs to be dynamic? Will the table's structure change often?
Here's a way I did it purely with MySQL:
SET SESSION group_concat_max_len = 2048;
SELECT GROUP_CONCAT(CONCAT(" ",CAST(column_name as CHAR(50)))) FROM information_schema.columns WHERE table_name='real_big_table' AND column_name LIKE 'prefix%' INTO #sub;
SET #x = CONCAT("SELECT ",#sub," FROM my_db.real_big_table WHERE my_db.real_big_table.country_id='US'");
PREPARE stmt FROM #x;
EXECUTE stmt;
My answer is inspired by this answer.
Note: My 'inner-DBA' (in the form of a small angel on my shoulder) tells me that needing to do this is probably a sign of bad DB structure, but my 'inner-hacker' (in the form of a small devil on my other shoulder) says "just get it done!"
Convert your database to one with three columns:
Product_type (vegetable, fruit, etc)
Name (apple, carrot, etc)
color (orange, yellow, etc)
Now you can use your wildcard to obtain only a certain type, because it now is IN a columns, not in the header.
How about creating the query in 2 steps?
1- Get the column's name from the db schema (or elsewhere)
2- Generate an sql query with the column's name that match your filter condition.

PHP/MySQL - How do you determin the field names from a given query result?

Given a result set, how can I determin the actual names of the fields specified in the query (NOT their aliases).
$query = "SELECT first AS First_Name, last AS Last_Name FROM people";
$dbResult = mysql_query($query);
$fieldCount = mysql_num_fields($dbResult);
for ($i=0; $i<$fieldCount; $i++) {
// Set some values
$fieldName = mysql_field_name($dbResult, $i);
}
This example returns field names, but in this example it returns the alias "First_Name" instead of the actual field name "first".
Is it possible to get the actual field name from such a query. Particularly if I am writing a function and have no idea what query will be thrown at it.
If you are using MySQLi:
http://www.php.net/manual/en/mysqli-result.fetch-field.php
The field object has a "orgname" property.
The "classic" MySQL equivalent function doesn't report back the original column names.
Short answer: you don't.
Long answer: Once the dataset is pulled by MySQL and sent back to PHP, the only information PHP now has is the columns, or aliases if you used them. There is no way to look at a result set and determine what the original column names were. You have to switch to another DB driver like mysqli to obtain this info.
Your question doesn't make sense.
What are you going to do if you get a derived column i.e.
select column_a + column_b as order_total from orders;
are you saying you want to know that the original query was column_a + column b ??
if so, you probably need to write a query parser, or get one off the internet.
I think the implementation of that is beyond the scope of your question though :)
I'm not 100% sure about this, but I would say: there is no way.
The MySQL gives you back the result set, nothing more. It does not return the select statement nor any details about it.
So you cannot get the original field names because the server will provide you the information you asked: alias names.
If you don't mind making a second query (and your using MySQL 5 or greater) you can ask information_schema for the names.
Check out MySQL Reference for the details:
SHOW COLUMNS FROM tbl_name;
if you have access to the string of the query you could try a regular expression to parse it.
I'm no regex master but you could chop up the string by looking at the text between 'select' and 'from' then grabbing all the field names as either
field FieldAlias
or
field as FieldAlias
If you're trying to write some functionality to let you know what fields are being fetched for handling updates - the only way to do this correctly is for it to present an SQL-less interface to the code above and manage all SQL generation itself. This is called a data abstraction layer.

Categories