Mysql join 2 table with same column name without using an alias - php

I got this example, but I want to know in an overall way, how to fetch 2 column with the same name in MySQL without using an alias?
Example:
$query = "SELECT app.*, categorie.* FROM app JOIN categorie ON app.id = categorie.id";
while ($row = mysql_fetch_array($query)) {
$categorie = $row[categorie.name]; //is there a way to do this?
$aplicativo = $row[app.name];
}
This example is simply solved with an alias, but is there another way, something like the example? Thank you for your time

mysql_fetch_array
Warning
This extension is deprecated as of PHP 5.5.0, and will be removed in the future. Instead, the MySQLi or PDO_MySQL extension should be used. See also MySQL: choosing an API guide and related FAQ for more information. Alternatives to this function include:
mysqli_fetch_array()
PDOStatement::fetch()
If two or more columns of the result have the same field names, the last column will take precedence. To access the other column(s) of the same name, you must use the numeric index of the column or make an alias for the column. For aliased columns, you cannot access the contents with the original column name.
Example #1 Query with aliased duplicate field names
SELECT table1.field AS foo, table2.field AS bar FROM table1, table2
So in your case the query should be, as the documentation suggests, using alias or numeric index:
SELECT app.name as "app.name",... , categorie.name as "categorie.name",... FROM app JOIN categorie ON app.id = categorie.id

Use PDO's FETCH_NUM type when fetching your rows to get a 0 indexed array that will contain both values, even if they have the same column name.
function selectStuff(\PDO $pdo)
{
$stmt = $pdo->prepare('SELECT app.name, categorie.name FROM app JOIN categorie ON app.id = categorie.id');
$stmt->execute();
foreach ($stmt->fetchAll(\PDO::FETCH_NUM) as $row) {
// $row[0] contains app.name
// $row[1] contains categorie.name
}
}
You have to leverage a 0 indexed fetching of columns to be able to do that without alias. The reason is simple: you can't logically access two different values in an array if they share the same key.
Notice that I explicitely named the columns to be selected. You should prefer that to selecting all fields, for readability reason, and also (arguably this is in most cases not noticeable) for performance reasons.

Disclaimer: mysql_fetch_array()
I understand this is not what should be recommended theese days, but I havent moved away as of yet since its still running great on any PHP 5.x server around where I'm from. Soon the time is there so I can refactor my codebase, maby even sooner for all I know. That being said...
Back to your question...
I was googling and looking for something else I see after reading, but I am beginning to understand that the entire approach of being able to read what you are asking for has no practical use in real world. That is, compared to changing your query adding some aliases like this:
$query = "SELECT app.name AS appname, categorie.name AS categoriename, app., categorie. FROM app JOIN categorie ON app.id = categorie.id";
This way your returned query returns more fields with unique names so you can access them like you ask for.
But say you were looking for a way to accomplish what you ask without changing your sql-query. You need to know the order of results, especially if there are more than one ambiguous name in the list as you will not be able to reorder the ambiguous fields, MySQL does return all your fields you see its just the way an array works you do not get the answers you want. Or, my disclaimer, this is one of the reasons I should move away from this MYSQL_FUNCTIONS... (I hope so because if so this is a damn great reason alone, mysql_fetch_assoc() should have an option to lay ouu an array of results as for this example would be ['id','name','id','name'])
You do not say anthing about your DB however say they both have two columns id and name for this example. Return results for each database would be id, name or in the case of your query it would be : id, name, id, name
How does this work? A resulted array from mysql_fetch_array() looks like this:
Say app db has values: 1, app name
Say category db has values: 2, category name
Array
(
[0] => 1
[id] => 2
[1] => 'app name'
[name] => 'category name'
[2] => 2
[3] => 'category name'
)
So if you know the order of results you can atleast access the different fields even if they have the same name. However looking at the simple example above it would be hard to say if [2] is [id] or [name] but if you know the order...
Then again, select * and someone changes the database... adds a column...

$query = "SELECT app.name as `appname`, categorie.name as `catname`,
and_maybe_other_columns_here
FROM app JOIN categorie ON app.id = categorie.id";
while ($row = mysql_fetch_array($query)) {
$categorie = $row['appname']; //is there a way to do this?
$aplicativo = $row['catname'];
}

Related

Column 'code' in where clause is ambigous

I need a bit help with a query. I want extract the content from three different table, this table have different records so the code key is different agains the other table. The table contains the credentials for the login, I need to check if the code and the email is found in almost one table, this is an example:
$Query = "SELECT * FROM login_tb1, login_tb2, login_tb3
WHERE (code = '".mysql_real_escape_string($code)."') OR
(email = '".mysql_real_escape_string($email)."') ";
Now when I execute this query I get this error:
Column 'code' in where clause is ambigous
I can't perform a JOIN 'cause the column code is different, all the records are different. All the table have the same structure anyway. How can I fix this?
Thanks.
It took me a while to understand that error message as well. Ambigous is such a fancy word. In human language, it means that MySQL is trying to tell you: "Hey man, the column exists in multiple tables in the select clause, so I don't know which table you mean!". You should be explicit about the table:
$Query = "SELECT * FROM login_tb1, login_tb2, login_tb3
WHERE (login_tb1.code = '".mysql_real_escape_string($code)."') OR
(login_tb1.email = '".mysql_real_escape_string($email)."') ";
If you don't care about which table and they have the same schema, you still need to be explicit, so your query will be a bit more advanced:
SELECT * FROM login_tb1 as l1, login_tb2 as l2, login_tb3 as l3
WHERE ( l1.code = 'code' OR l1.email = 'email' )
OR ( l2.code = 'code' OR l2.email = 'email' )
OR ( l3.code = 'code' OR l3.email = 'email' )
Side note: I don't know your exact use case, but it seems a bit like an anti-pattern that you have multiple login tables with the seemingly same schema. Unless you have a very specific reason not to, you should keep it in a single table.
Code column would be part of more than one table.
in this case prefix with anyone of the table..
e.g login_tb1.code
this would do the trick
You need to tell mysql which code column to use.
Apparently, that column appears in more than one table, so for each table you want to check the code column, write it like this:
tablename.code
This way mysql will know which column to use
I also suggest taking a look at your ta le structure, since having 3 tables with the same structure seems weird.
You can post a question a out the as well or elaborate.
There are similar columns in the tables. You need to properly alias the table
$Query = "SELECT t1.col,t2.col,t3.col FROM login_tb1 as t1,
login_tb2 as t2, login_tb3 as t3
WHERE (t1.code = '".mysql_real_escape_string($code)."') OR
(t2.email = '".mysql_real_escape_string($email)."') ";

Multiple queries or can be done in one?

I'm not very experienced with more advanced MySQL query stuff.. (mostly basic queries, return and parse response..etc)
However.. I am not clear on the correct approach when I need multiple things (responses) from the database.. Is there a way to get these things from the single query? or do I need to do a new query for each time?
Background:
I use PDO to do a SELECT statement
ie:
$getAllVideos_sql = "SELECT * as FROM $tableName WHERE active IS NOT NULL OR active != 'no' ORDER BY topic, speaker_last, title;";
$getAllVideos_stmt = $conn->prepare($getAllVideos_sql);
$getAllVideos_stmt->execute();
$getAllVideos_stmt->setFetchMode(PDO::FETCH_ASSOC);
$results = $getAllVideos_stmt->fetch(PDO::FETCH_ASSOC);
//parse as I see fit
This gives me my 'chunk of data' that I can pick apart and display as I want.
However.. I want to also be able to give some stats (totals)
For the total (distinct) 'topics'.. as well as total count for the 'titles' (should all be unique by default)
Do I need to do another query, prepare, execute, setFetchMode, fetch all over again?
Is this the proper way to do this? Or is there a way to crib off the initial commands that are already in play?
To be clear, I'm not really looking for a query... I'm looking to understand the proper way one does this.. when they need several pieces of data like I do? multiple queries and executions..etc?
Or maybe it can and -should- be done in one snippet? With an adjustment to the query itself to return sub select/queries info?
this isnt the correct syntax, because it only returns 1 record..(but the total topic count seems to be correct, even though I only get 1 record returned)
SELECT *, count(DISTINCT topic)as totalTopics, count(DISTINCT title)as totalTitles FROM $tableName;
Maybe this the more proper approach? Try to include these totals/details in the main query to pick out?
Hope this makes sense.
Thanks
I don't think you're going to get anything very clean that'll do this, however something like this might work:
SELECT * from $Table t
INNER JOIN (
SELECT COUNT(DISTINCT Topic) as TotalTopics FROM $Table
) s ON 1 = 1
INNER JOIN (
SELECT COUNT(DISTINCT Title) as TotalTitles FROM $Table
) f ON 1 = 1
WHERE ( Active IS NOT NULL ) AND Active != 'no'
Especially with web applications, many people are regularly doing counts or other aggregations somewhere along the way. Sometimes if it is a global context such as all topics for all users, having some stored aggregates helps rather than requerying all record counts every time.
Example. If you have a table with a list of "topics", have a column in there for uniqueTitleCount. Then, based on a trigger, when a new title is added to a topic, the count is automatically updated by adding 1. You can pre-populate this column by doing a correlated update to said "topics" table, then once the trigger is set, you can just have that column.
This also works as I see many times that people want "the most recent". If your system has auto-increment IDs in the tables, similarly, have the most recent ID created for a given topic, or even most recent for a given title/document/thread so you don't have to keep doing something like.
select documentID, other_stuff
from sometable
where documentID in ( select max( documentID )
from sometable
where the title = 'something' )
Use where these make sense then your optimization pull-downs get easier to handle. You could even have a counter per document "title" and even a most recent posting date so they can quickly be sorted based on interest, frequency of activity, whatever.

PHP mysqli query returning result "1" on joins

I have been trying to pull out some values from php-mysql, and it's returning a strange result "1". It comprises of an inner join with a where clause, everything works fine, but the result is strangely "1" when I try to echo out the ID, but I am assured that the ID is not 1. When I run the same query on the MySQL CLI, it returns exactly what I need but in the php, it doesn't even say there's an error.
Interestingly, only the ID is returning "1", everything else is returning the correct values. The ID on the CLI is- "V8FBaMJT6bqPbpRutJgkRdc44S3Gz3H8VjW5iu5E4yhBlLA1/D8o+EcGMUY62LZLrxb2SSkaxBoUwaiQXQv+3OsoDTuheYTy4ibPsy91X8JhNGFjOWM2MWQyMWMxMTBmMTU5YjU5NzU2NGM3OTc1YQ==", so there's no chance that it is equal to "1".
$showPosts = "SELECT * FROM box
INNER JOIN interactions
ON box.u_id=interactions.actorid
WHERE interactions.actorid=?
AND interactions.type IN ('1','5')
ORDER BY box.time_created";
if ($stmt = $conn->prepare($showPosts)) {
/* bind parameters for markers */
$b = "V8FBaMJT6bqPbpRutJgkRdc44S3Gz3H8VjW5iu5E4yhBlLA1/D8o+EcGMUY62LZLrxb2SSkaxBoUwaiQXQv+3OsoDTuheYTy4ibPsy91X8JhNGFjOWM2MWQyMWMxMTBmMTU5YjU5NzU2NGM3OTc1YQ==";
$stmt->bind_param("s", $b);
/* execute query */
$stmt->execute();
/* bind result variables */
$metaResults = $stmt->result_metadata();
$fields = $metaResults->fetch_fields();
$statementParams = '';
//build the bind_results statement dynamically so I can get the results in an array
foreach($fields as $field){
if(empty($statementParams)){
$statementParams .= "\$post['".$field->name."']";
} else {
$statementParams .= ", \$post['".$field->name."']";
}
}
$statment = "\$stmt->bind_result($statementParams);";
eval($statment);
while($stmt->fetch()){
echo $post['id'];
echo $post['time_created'];
}
}
Now, in the result- the first is the strange "1" and the second is the timestamp.
Also, if I remove the INNER join part, and do a simple SELECT * FROM box, it returns the ID perfectly. And I have also tried doing a stmt->num_rows, and it returns '0'. Maybe I am missing something obvious.
I am new to using joins in php mysql and it has been a big headache for me for the last 2 hours, any help is appreciated.
Thank you.
The problem is that you use SELECT * FROM ... which returns every column from any table you are using. This is not what you want. Always write the columns you want to read from the query.
In this case you have more than one Id column in your result set, one from the table box, the other one from the table interactions. The value of $post['id'] will contain the value of the interactions table. So, change the SELECT query by writing:
SELECT box.id, box.time_created FROM ...
Also, do not use the eval() function, ever. You might want to switch to PDO which has a more clear API (the PDOStatement::fetch function returns the next row as an array)
After hours of research, I have come to an working example and it works just fine.
Someone else might face the same problems in the future and make mistakes, so I am posting an answer to the problem here:
Problems:
Having same column names in two joined tables
The first problem was the the table interactions had the same column name id as that of the table box. Since I used SELECT * FROM box along an inner join with interactions, it resulted in the return of the results based on the second id which was of interactions rather than box.
Not storing the results before showing record count
Secondly, the problem was the results were not being stored as in http://php.net/manual/en/mysqli-stmt.num-rows.php, which was a silly mistake.
Solutions:
Addressing the column repition
So, firstly I decided to change the column name of the table interactions for id, and I changed it to inter_id to avoid further conflicts. Though not a very smart step, it will avoid such silly mistakes in the future. Then as pointed out by the previous answer, I had to specify the column names of the results I wanted to output rather than using a SELECT *, so I changed it to
SELECT box.id, box.time_created FROM box
INNER JOIN interactions
ON box.u_id=interactions.actorid
WHERE interactions.actorid=?
AND interactions.type IN ('1','5')
ORDER BY box.time_created";
That pretty much solved the first problem.
Counting the number of results
The second one was just about adding the $stmt->store_result(); to the code and it worked flawlessly just before I did a echo $stmt->num_rows; and it showed the results perfectly.
Thank you, again.

Dynamic MySQL query: Join results from numerous tables

Something I've been stumbling on for a while now. I have a table called gears which contains rows with the names: id, mid, cid and installed. I want to search this table and return in csv format a list of mids for some unique cid. For example if cid = $cid I can use:
$query = $database -> query("SELECT COUNT(mid), GROUP_CONCAT(mid) FROM gears WHERE cid=$cid", __LINE__, __FILE__);
$gears_installed = $database -> get_result($query);
$gears = $database -> get_result($query, 0, 1);
Don't worry about the function names, they do exactly as one would expect. So if there were 3 rows for that specific $cid, with mids: bank, lottery and post then $gears_installed would be equal to 3 and $gears would be equal to bank,lottery,post. This works as intended.
Now on to the question I have. Each unique mid has its own table, named settings_mid_here. I.e, for the above three, I have the tables settings_bank, settings_lottery and finally settings_post. Each of these tables will also have a column called cid (this is how the two can be related). How do I go about running one query to return the entire row from each table where cid=$cid? I do not want to run a separate query for SELECT * FROM settings_bank WHERE cid=$cid and SELECT * FROM settings_post WHERE cid=$cid and finally SELECT * FROM settings_post WHERE cid=$cid, as this could result in around 10 extra queries on one page load (there are, at the moment, 10 different mids).
As you can see, the problem is dynamic. It must be able to adapt to a different number of mids, somehow differentiate the settings within each table (for example settings_bank may have a column with name name, and so might settings_post). Finally, it must also be able to return a default row (not null values) if there does not exist a row corresponding to the given $cid.
A complicated task but I hope someone can help me with this as I have not been able to get anywhere.
$queries = array();
foreach(explode(',', $gears) as $gear) {
$queries[] = "SELECT '$gear' AS gearname, settings_$gear.* FROM settings_$gear WHERE cid=$cid";
}
$sql = implode(' UNION ', $queries);
$query2 = $database->query($sql);
This query will return one row for each table, with an extra gearname column to indicate which table that row came from.
Or you can create a JOIN dynamically:
$gears_array = explode(',', $gears);
$joins = implode(' JOIN ', $gears_array);
$wheres = implode(' AND ',
array_map(function($g) use ($cid) {
return "$g.cid = $cid";
}, $gears_array));
$sql = "SELECT * FROM $joins WHERE $wheres";
$query2 = $database->query($sql);
This is not really an answer to your specific question, simply because there's no way to accomplish what you are trying with one query.
The reason is simple: RDBMSs are not designed to work this way. Tables are supposed to store data that represent entities and relations. In your case, for each distinct value of mid, a table named settings_{mid} must exist, thus forcing the mid column implicitly store (a part of) a table name. But that's not data, that's metadata.
That would not really be a problem if SQL syntax could accept variable, parametrized, column-related or arbitary table names. But it doesn't. And that's by design. Instead, an RDBMS provides you with all the tools you could ever need to relate your data to each other. By using it the intended way, you'll never have to resort to such 'dynamic' tricks.
In your case, there should be one config table with a mid column to distinguish the rows that refer to the specific mid value. Then, the query would be simple:
select * from `config` where mid='$mid' and cid='$cid'
This is the relational way. Thus the R in RDBMS. There's absolutely no reason at all to mix data with metadata. If you do, you move the relation resolution problem in higher levels of the application model.
And one last thing: One might argue that the config_{mid} tables might have similar but not identical structure. There's a solution for that too: IS-A relations.
Having said that, for your specific problem, a solution along the lines of Barmar's answer would do the trick.

Select MySQL row value as column title?

I'm trying to do a customizable and extendable profile system for my CMS. From the user perspective it is straight forward, but for the admin I want all data, including the profile data, to be searchable. Profile fields may be added by "plugins", which may also add new fields to search on. I don't know if what I'm trying to do with MySQL to make this work is possible or if I'm going at it completely the wrong way.
So I have the users stored in one table (users), with columns for id, email, password and access_level.
I then have another table with profile information (profiles), stored as user_id, parameter and value. The parameter could eventually be put into a separate table again (so it isn't repeating itself), but for now I'll leave it like this.
The parameter and value are basically the profile data. For example, parameter may be "age" and the value may be "22".
What I want to try and do, is select the users table, with the profile information joined so the parameter is mapped to an additional column. So it ends up like so, straight from MySQL:
id email password access_level age
1 a#a.com ***** 1 22
2 b#b.com ***** 2 25
3 c#c.com ***** 2 25
I've been looking at pivot tables all afternoon, but from all I can see the "column name" is pre-defined. In this case I want the "column name" to come from the row itself.
If it isn't possible to do it with a single query, what other methods are there? I'm using PHP if the best method is to do it via that.
Any suggestions would be much appreciated. :)
Well, if you need to know the column names in advance, you can query the information_schema database:
SELECT COLUMN_NAME
FROM information_schema.COLUMNS
WHERE TABLE_NAME='your table'
However, that gets the raw column names. If you're aliasing in your query, you'll have to fetch them indirectly:
SELECT somefield AS alias1, otherfield AS alias2
FROM ...
and then
$stmt = mysql_query($query);
$first = true;
while($row = mysql_fetch_assoc($stmt)) {
if ($first) {
$column_names = array_keys($row);
... display column names here
$first = false;
}
... output row here
}

Categories