PDO changes query - php

I want to have result of my query as it was before I replaced db connection using PDO. How can I get the query as i t was before I implemented PDO?
This is my query:
$query =
"SELECT
`id_affirmation`,
`affirmation`,
`author`,
`user_rate`,
am.date,
am.time,
hua.date,
hua.time
FROM `affirmation_male` am
JOIN `history_user_affirmation` hua ON am.id_affirmation = ua.affirmation_id
WHERE hua.user_id = '" . $id_user . "'
ORDER BY
STR_TO_DATE(hua.date, '%d-%m-%Y') DESC,
hua.time DESC";
For some reason the result of query when I use PDO is i got date from affirmation_male. Do you know why?

Your query returns two columns that have the same name, hence PDO gets lost when it fetches the results; since each records is represented as an associative array, duplicate keys generate ambiguity (only one key will be retained).
You would need to alias those columns to remove ambiguity:
$query =
"SELECT
`id_affirmation`,
`affirmation`,
`author`,
`user_rate`,
am.date am_date,
am.time am_time,
hua.date AS hua_date,
hua.time AS hua_time
FROM `affirmation_male` am
JOIN `history_user_affirmation` hua ON am.id_affirmation = hua.affirmation_id
WHERE hua.user_id = '" . $id_user . "'
ORDER BY
STR_TO_DATE(hua.date, '%d-%m-%Y') DESC,
hua.time DESC";
Notes:
it would also be a good idea to prefix the first columns in the query with the alias of the table they belong to, as this makes the query more readable (and will avoid conflicts if ever these columns names were available in more than one table coming into play in the query)
you could remove backticks to make the query more readable, as the column and table names that you are quoting do not seem to contain any special characters

Related

Fetch data based on another MySQL query

I have the following two queries. The first query is fetching a key called srNumber from first table called tags and then the second query is using that srNumber to fetch details from a second table called nexttable.
$tagQuery = "SELECT * FROM tags WHERE status = 0 AND currentStage = '1' AND assignedTo = '1' ORDER BY
deliveryDate ASC";
$tagQueryExecute = mysqli_query($conn, $tagQuery);
while($rows = mysqli_fetch_array($tagQueryExecute)){
$srNumber = $rows['srNumber'];
$nextQuery = "SELECT * FROM nexttable WHERE srNumber='$srNumber'";
$nextQueryExecute = mysqli_query($conn, $nextQuery);
$detailsFromNextTable = mysqli_fetch_array($nextQueryExecute);
//Show these details
}
For a small result this is not a big issue. But if the first query got so many results, then second query has to run as many times as number of loop. Is there any other way to do this efficiently?
NB: Please ignore the SQL injection issues with these queries. I just simplified it to show the problem
As you appear to have only 1 row in the second table, you would be better off with a join, MySQL: Quick breakdown of the types of joins gives some more info on the types of joins.
SELECT *
FROM tags t
JOIN nexttable n on t.srNumber = n.srNumber
WHERE t.status = 0 AND t.currentStage = '1' AND t.assignedTo = '1'
ORDER BY t.deliveryDate ASC
This also removes the SQL injection as well.
I would also recommend removing the * and just list the columns you intend to use, this also helps if you have columns with the same names in the different tables as you can add an alias to the specific columns.
FYI - the original problem you have is similar to What is the "N+1 selects problem" in ORM (Object-Relational Mapping)?

Using Replace in SELECT * statement

How can I work this REPLACE statement I'm using in the following, into my SELECT statement below ? The update works perfectly, but need the same code in the SELECT.
$sql1 = ("UPDATE $table SET notes=replace(REPLACE(notes,CHAR(13),' '),CHAR(10),' ') WHERE year='$year'");
$sql2 = "SELECT * FROM $table WHERE SUBSTR(week_start_date,-4)=$startDate AND week_num = '$week' AND archived!='yes' ORDER BY fn,ln";
Thanks for any assistance.
If you have data in your table that has CRLF that you want to REPLACE with ' ', you can't use the SELECT * notation - you have to tell it which column you want to replace the characters in. You'll have to do something like this:
$sql2 = "SELECT REPLACE(REPLACE(notes, CHAR(13), ' '), CHAR(10), ' ') as fixed_notes FROM $table ...`
In general, it's best to avoid using SELECT *, and always to specify the columns you want (as well as any scalar functions you want to run on said columns) explicitly. This way, if the table definition adds more columns, you don't start getting data you never intended to process; and if a column gets dropped, you'll know that was the cause rather than something else in your presentation layer. It also means that it'll be possible to manipulate the individual columns in the statement where appropriate.
If you can live with these limitations, and you don't mind having the column appear twice, you could do this:
$sql2 = "SELECT *, REPLACE(REPLACE(notes, CHAR(13), ' '), CHAR(10), ' ') as fixed_notes FROM $table ...`

PHP: While loop not working after adjusting SELECT for SQL injection prevention

I am trying to set up PHP queries for MySQL in a way to prevent SQL injection (standard website).
I had a couple of INSERT queries where changing this worked well but on the following SELECT I keep getting an error since the update and it looks like the while loop doesn't work with the changes I made (it works well without using the statement as in the old code).
Can someone tell me what I am doing wrong here ?
New PHP:
$stmt = $conn->prepare("SELECT ? FROM TranslationsMain WHERE location LIKE '%calendar weekday%' ORDER BY sortOrder, ?");
$stmt->bind_param('s', $selectedLang);
$stmt->execute();
$result = $stmt->get_result();
while($arrCalWeekdays = $result->fetch_assoc()){
$calWeekdays[] = $arrCalWeekdays;
}
$conn->close();
Old PHP (changed part):
$sql = "SELECT " . $selectedLang . " FROM TranslationsMain WHERE location LIKE '%calendar weekday%' ORDER BY sortOrder, " . $selectedLang;
$result = $conn->query($sql);
while($arrCalWeekdays = $result->fetch_assoc()){
$calWeekdays[] = $arrCalWeekdays;
}
$conn->close();
Error message:
Fatal error: Call to a member function fetch_assoc() on a non-object in /homepages/21/d580042014/htdocs/myform.php on line 21
Many thanks in advance.
You cannot bind column and table names, only data. You need to specify the table and then bind for your '%calendar weekday%'.
$stmt = $conn->prepare("SELECT " . $selectLang . " FROM `TranslationsMain` WHERE `location` LIKE ? ORDER BY `sortOrder`, " . $selectedLang);
$stmt->bind_param('s', $calendar_weekday);
If you want to use dynamic table / column names you should perform the minimal white-listing of those items. You can build a dynamic white list by asking the database what columns are valid for a given database table. For example:
SELECT `COLUMN_NAME`
FROM `INFORMATION_SCHEMA`.`COLUMNS`
WHERE `TABLE_SCHEMA` = `database_name`
AND `TABLE_NAME` = `table_name`
You could place all of this information in arrays and then check to make sure the table / column names used in the query are in the arrays. Extra consideration for table and column names should be performed, making sure that no key / reserved words are used for these names.
Finally, use backticks around the validated table / column names when calling the values for the dynamic queries. This will cover any potential changes to the key / reserved words list and provides an additional layer of protection.

Attempt to update one mySQL table with data from two other tables is failing

I am trying to update one table which holds the inputted data from my employees. The table has three columns: entry id, field id, and value. I am wanting to make sure there is a row for every field in the this table according to each entry. As such I have the written the following php/sql script. The thought process was get two arrays (one containing the entry ids and the other the field ids) and check the input table to see if there was an existing row for every field and entry. If not then use an inert into command to add a row with the value 0, but this command is failing. My guess is a timeout error due to the intensity of this function. Any suggestions?
$db = JFactory::getDBO();
$field_query = 'SELECT id FROM `jos_directory_field`';
$db->setQuery( $field_query );
$fields = $db->loadObjectList();
$entry_query = 'SELECT id FROM `jos_directory_entry`';
$db->setQuery( $entry_query );
$entries = $db->loadObjectList();
for($e=0;$e count($entries);$e++) {
for($i=0;$i count($fields);$i++) {
$insert_query = 'INSERT INTO `jos_directory_enf` (entry_id,field_id,field_value)';
$insert_query .= 'SELECT '.$entries[$e]->id.','.$fields[$i]->id.',0';
$insert_query .= 'FROM dual WHERE NOT EXISTS (SELECT * FROM `jos_directory_enf`';
$insert_query .= 'WHERE entry_id = '.$entries[$e]->id.' and field_id = '.$fields[$i]->id.')';
$db->setQuery( $insert_query );
$db->query();
}
}
It's too bad Joomla doesn't appear to support prepared statements, as they can help with performance for repeated queries.
However, in this case there's a better option. You should be able to replace the PHP code with a single SQL statement, making use of the IGNORE clause for INSERT statements. First, make sure you have a unique index on the entry_id and field_id columns of jos_directory_enf. Then the SQL statement would be something like:
INSERT IGNORE INTO `jos_directory_enf` (entry_id,field_id,field_value)
SELECT e.id, f.id, 0
FROM jos_directory_entries AS e
JOIN jos_directory_field AS f
;
.

How to Output the results of a MySQL query that used aliases?

I have two primary MySQL tables (profiles and contacts) with many supplementary tables (prefixed by prm_). They are accessed and manipulated via PHP.
In this instance I am querying the profiles table where I will retrieve an Owner ID and a Breeder ID. This will then be referenced against the contacts table where the information on the Owners and Breeders is kept.
I received great help here on another question regarding joins and aliases, where I was also furnished with the following query. Unfortunately, I am having huge difficulty in actually echoing out the results. Every single site that deals with Self Joins and Aliases provide lovely examples of the queries - but then skip to "and this Outputs etc etc etc". How does it output????
SELECT *
FROM (
SELECT *
FROM profiles
INNER JOIN prm_breedgender
ON profiles.ProfileGenderID = prm_breedgender.BreedGenderID
LEFT JOIN contacts ownerContact
ON profiles.ProfileOwnerID = ownerContact.ContactID
LEFT JOIN prm_breedcolour
ON profiles.ProfileAdultColourID = prm_breedcolour.BreedColourID
LEFT JOIN prm_breedcolourmodifier
ON profiles.ProfileColourModifierID = prm_breedcolourmodifier.BreedColourModifierID
) ilv LEFT JOIN contacts breederContact
ON ilv.ProfileBreederID = breederContact.ContactID
WHERE ProfileName != 'Unknown'
ORDER BY ilv.ProfileGenderID, ilv.ProfileName ASC $limit
Coupled with this is the following PHP:
$owner = ($row['ownerContact.ContactFirstName'] . ' ' . $row['ownerContact.ContactLastName']);
$breeder = ($row['breederContact.ContactFirstName'] . ' ' . $row['breederContact.ContactLastName']);
All details EXCEPT the contacts (gender, colour, etc.) return fine. The $owner and $breeder variables are empty.
Any help in settling this for me would be massively appreciated.
EDIT: My final WORKING query:
SELECT ProfileOwnerID, ProfileBreederID,
ProfileGenderID, ProfileAdultColourID, ProfileColourModifierID, ProfileYearOfBirth,
ProfileYearOfDeath, ProfileLocalRegNumber, ProfileName,
owner.ContactFirstName AS owner_fname, owner.ContactLastName AS owner_lname,
breeder.ContactFirstName AS breeder_fname, breeder.ContactLastName AS breeder_lname,
BreedGender, BreedColour, BreedColourModifier
FROM profiles
LEFT JOIN contacts AS owner
ON ProfileOwnerID = owner.ContactID
LEFT JOIN contacts AS breeder
ON ProfileBreederID = breeder.ContactID
LEFT JOIN prm_breedgender
ON ProfileGenderID = prm_breedgender.BreedGenderID
LEFT JOIN prm_breedcolour
ON ProfileAdultColourID = prm_breedcolour.BreedColourID
LEFT JOIN prm_breedcolourmodifier
ON ProfileColourModifierID = prm_breedcolourmodifier.BreedColourModifierID
WHERE ProfileName != 'Unknown'
ORDER BY ProfileGenderID, ProfileName ASC $limit
Which I could then output by:
$owner = ($row['owner_lname'] . ' - ' . $row['owner_fname']);
Many Thanks to All!
I guess you're using the mysql_fetch_array or the mysql_fetch_assoc-functions to get the array from the result-set?
In this case, you can't use
$row['ownerContact.ContactFirstName']
as the PHP-Docs read:
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.
So, you can either use an AS in your SQL-query to set other names for the doubled rows or use the numbered indexes to access them.
This could then look like this:
Using AS in your Query
In your standard SQL-query, the columns in the result-set are named like the columns which their values come from. Sometimes, this can be a problem due to a naming-conflict. Using the AS-command in your query, you can rename a column in the result-set:
SELECT something AS "something_else"
FROM your_table
This will rename the something-column to something_else (you can leave the ""-quotes out, but I think it makes it more readable).
Using the column-indexes for the array
The other way to go is using the column-index instead of their names. Look at this query:
SELECT first_name, last_name
FROM some_table
The result-set will contain two columns, 0 ==> first_name and 1 ==> last_name. You can use this numbers to access the column in your result-set:
$row[0] // would be the "first_name"-column
$row[1] // would be the "last_name"-column
To be able to use the column-index, you'll need to use mysql_fetch_row or the mysql_fetch_assoc-function, which offers an associative array, a numeric array, or both ("both" is standard).
you need to replace the * with the data you need , and the similar ones you have to make aliases too :
ownerContact.ContactFirstName as owner_ContactFirstName
and
breederContact.ContactFirstName as breeder_ContactFirstName .
like this :
select ownerContact.ContactFirstName as owner_ContactFirstName , breederContact.ContactFirstName as breeder_ContactFirstName from profiles join ownerContact ... etc
in this way you will write :
$owner = ($row['owner_ContactFirstName'] . ' ' . $row['owner_ContactLastName']);
$breeder = ($row['breeder_ContactFirstName'] . ' ' . $row['breeder_ContactLastName']);
You cannot specify table alias when you access row using php. Accessing it by $row['ContactFirstName'] would work if you didn't have 2 fields with the same name. In this case whatever ContactFirstName appears second overwrites the first.
Change your query to use fields aliases, so you can do $owner = $row['Owner_ContactFirstName'].
Another option I'm not 100% sure is to access field by index, not by name(e.g. $owner=$row[11]). Even if it works I don't recommend to do so, you will have a lot of troubles if change your query a bit.
On outer select You have only two tables:
(inner select) as ilv
contacts as breederContact
there is no ownerContact at all

Categories