I'm having a problem with a MySQL query that uses inner and left joins. The problem is that, where the foreign key in the primary table is blank in the case of a NULL-permitted field, the query doesn't read all the fields in the primary table.
My task is to organize a list of recordings with MySQL, some of which are live recordings while others are studio recordings. For the purposes of this question, I'm simplifying the table structure as follows:
table name: recordings
fields:
recording_id INT AUTO-INCREMENT
recording_name VARCHAR NOT NULL
artist_id INT NOT NULL FOREIGN KEY
event_id INT NULL FOREIGN KEY
table name: artists
fields:
artist_id INT AUTO-INCREMENT
artist_name VARCHAR NOT NULL
table name: events
fields:
event_id INT AUTO-INCREMENT
event VARCHAR NOT NULL
venue_id INT NOT NULL FOREIGN KEY
table_name: venues
fields:
venue_id INT AUTO-INCREMENT
venue_name VARCHAR NOT NULL
address VARCHAR NOT NULL
Where a recording was done live, I want the option to give details of the event where the recording was done, and if it was a studio recording, I leave the event field blank. In other words, in the recordings table event_id is an optional field, but artist_id is always required.
To edit an existing record in the recordings table, I have a form with three fields (again simplified):
<form>
<input name="recording_name" type="text" value="<?php $recording_name ?>" />
<select name="artist_id">
<option>Select option</option>
<option <?php $selected ?> value="1">Artist 1</option>
etc.
</select>
<select name="event_id">
<option>Select option</option>
<option <?php $selected ?> value="1">Venue 1</option>
etc.
</select>
</form>
I use the $selected variable to display the option corresponding with the existing value pulled from the database in the form's dropdown list, like so:
$selected = ($existing_value == $option_id ? 'selected="selected" : '');
Now, to get the existing values of the form I have the following SQL query:
$recording_sql =
'SELECT * FROM recordings
INNER JOIN artists ON recordings.artist_id = artists.artist_id
LEFT JOIN events ON recordings.event_id = events.event_id
LEFT JOIN venues ON events.venue_id = venues.venue_id'
Then, to populate the two dropdowns:
$artist_sql =
'SELECT * FROM artists'
$event_sql =
SELECT * FROM events
INNER JOIN venues ON events.venue_id = venues.venue_id
My PHP code looks something like this:
function buildForm($result){
$data = $result->fetch_array($MYSQLI_ASSOC))
$form = '<input name="recording_id" type="hidden" value="'.$data['recording_id'].'" />';
$form .='<input name="recording_name" type="text" value="'.$data['recording_name'].'" />';
$form .= buildSelectBox('artists', $data['artist_id']);
$form .= buildSelectBox('events', $data['event_id']);
return $form;
}
function buildSelectBox($table, $existing_id = NULL){
//run SQL to pull data from relevant table (i.e. $artist_sql, or $event_sql which includes join to 'venues')
//Loop through $mysqli_result to build each option
while(etc....){
$selected = ($existing_id == $option_id ? 'selected="selected" : '');
$options_list .= '<option'.$selected.'value="'.$id.'">'.$artist_name.'</option>';
}
return $options_list;
}
This works fine if both foreign keys have values. However, when the event_id field is blank in recordings, it doesn't read the other foreign key either. It reads the text field, recording_name, fine though. In other words, the result set I get for $recordings_sql contains only the value of the recording_name field, while both foreign keys are returned blank, even though one is not blank. I've tried all the join permutations (left, inner, right) in different combinations, but none of them give the desired result.
I'm stumped! Thank you in advance for any help!
I spent most of yesterday trying to reproduce the problem with a simplified version of my application, and eventually I did. It turns out that the first foreign key (artist_id in my simplified example), was repeated in another table lower down in a series of joins that start with the second foreign key (event_id in my simplified example). So, my guess is that, if 'event_id' is null, 'artist_id' gets overwritten by a null value because the query will return empty values for everything after 'event_id' if it is empty. So, the problem isn't with how LEFT JOIN works, but rather with the repetition of the first foreign key in a subsidiary table that depends on a second foreign key that is allowed to be null. I evidently need to revisit my table structure...
Many thanks for all the suggestions!
Related
I have two tables: TEMA e SOTTOTEMA.
In TEMA I have, as primary key, IDtema which is auto-increment.
I want it to be foreign key in the sottotema table and I wrote, in phpmyadmin, where I have my db,
ALTER TABLE sottotema ADD FOREIGN KEY (IDtema) REFERENCES tema (IDtema)
It doesn't give me errors, but the foreign key doesn't work.
I have predefined themes in a select option, and depending on the theme you choose, you can insert a sub-theme yourself.
<select id = "tema" name = "tema">
<option hidden></option>
<option value = "Animali"> Animali</option>
<option value = "Cucina"> Cucina </option>
<option value = "Sport"> Sport </option>
<option value = "Musica"> Musica </option>
<option value = "Cinema"> Cinema </option>
<option value = "Letteratura"> Letteratura </option>
</select></br>
<div id = "sottotema" style = "display:none">
<p id = "titolosottotema"> Sottotema </p>
<input type = "text" placeholder="Scrivi un sottotema" id = "st" name = "st"/>
</div>
All this obviously is inside a FORM and everything works, except for inserting the IDtema as a foreign key, in the sottotema table.
I report only the data entry queries in the db, but in my php code there is something else written, including the connection to the db obviously
<?php
$sottotema = $_POST['st'];
$query = "INSERT INTO sottotemi (nomeSottotema) VALUES ('$sottotema')";
$result = mysqli_query($mysqli, $query);
if (!$result){
echo "errore 1";
} else {
$query2 = "INSERT INTO blog (titoloBlog,nomeSottotema,nomeUtente,sfondo, font, colorefont) VALUES ('$titoloblog','$sottotema',(SELECT nomeUtente FROM utentiregistrati WHERE nomeUtente = '$nomeutente'),'$sfondo','$font','$colore');";
$result2 = mysqli_query($mysqli, $query2);
if(!$result2){
echo 'errore 2';
}
?>
In the db I have already entered my predefined themes, so the idtema, primary key, is already associated with a specific theme (eg 1 - Animali, etc.).
Please help me I'm desperate !!!!!
For the foreign key to work you need to specify the foreign table identifier in the INSERT operation, otherwise the row would be orphan form the start. Instead of INSERT INTO sottotemi (nomeSottotema) VALUES ('$sottotema') you'll need to find identifier (IDtema) to the foreign table (tema) and also provide it in the INSERT operation as follows (NOTE this is a different format of the INSERT statement that consumes the results of a SELECT statement instead of using the VALUES() version):
INSERT INTO sottotemi
(nomeSottotema, IDtema)
SELECT
'$sottotema', T.IDtema
FROM tema AS T
WHERE T.nome = '$tema'
I am using this auto complete form, that gets the data from 1 table,
now i am using that form to insert data from its table to another table.
here is my SQL for the inserting into the table "products"
$image = addslashes(file_get_contents($_FILES['prod_pic']['tmp_name']));
$sql="INSERT INTO `inventory` (`prod_brand`,`prod_name`,`prod_category`,`prod_price`,`prod_desc`,`prod_quantity`,`prod_pic`)
VALUES
('$_POST[prod_brand]','".mysql_real_escape_string($_POST['prod_name'])."','$_POST[prod_category]' ,'$_POST[prod_price]',
'".mysql_real_escape_string($_POST['prod_desc'])."','$_POST[prod_quantity]','{$image}')";
the prod_category is the column i need to fill. I have data from the table named "categories" with column name "categories"
so how do i input the data from categories to the column = prod_category in the products table?
Check example and try this way...may it's help you.
INSERT INTO student (s_id, s_name, s_email)
SELECT t_id, t_name, t_email FROM teacher
WHERE teaher.tid='25';
As i understand in POST['prod_category '] contain string, like you have text input. The best way to do what you need - is change category for select in html, like this
<select>
foreach(categories as $category){
<option value='category->id' >category1->name </option>
}
</select>
Then you will get in post category id from the table named "categories"
If you dont like it your should replace '$_POST[prod_category]' from youre query to subquery
select id from categories where categories = '$_POST[prod_category]'
I have a form to edit a record (specimen). On the form is a multiple select list which contains records from a table (topic). This select list shows topics as selected that exist for the specimen (as identified in the specimen_topic lookup table) as well as those that can be added to the specimen (from the topic table).
I want to be able to add topics not selected in the list to the lookup table where the topic_fk does not already exist for the specimen_fk:
CREATE TABLE IF NOT EXISTS `specimen_topic_lookup` (
`specimen_topic_lookup_pk` int(6) NOT NULL AUTO_INCREMENT,
`specimen_fk` int(6) NOT NULL,
`topic_fk` int(3) NOT NULL,
PRIMARY KEY (`specimen_topic_lookup_pk`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_swedish_ci AUTO_INCREMENT=8 ;
Any ideas how I can do this?
UPDATE
I have made the fields specimen_fk and topic_fk UNIQUE. Using the code below, only one record is created in specimen_table lookup, when two records should have been created (before making the fields UNIQUE, two records were created OK...). I assume this is because $specimen_pk is the same value for each insert.
foreach($topics as $topic){
$query_topics = "INSERT IGNORE INTO specimen_topic_lookup(specimen_fk, topic_fk)
VALUES ('$specimen_pk', '$topic')";
$result_topics = mysql_query($query_topics, $connection) or die(mysql_error());
}
Looks like having UNIQUE is stopping having a record made with the same value (which is at least what I expected...)
THIS WORKS
Without having to make specimen_fk OR topic_fk UNIQUE...
foreach($topics as $topic){
$query_topics = "INSERT INTO specimen_topic_lookup(specimen_fk, topic_fk)
SELECT '$specimen_pk', '$topic'
FROM DUAL
WHERE NOT EXISTS (SELECT 1
FROM specimen_topic_lookup
WHERE specimen_fk = '$specimen_pk' AND topic_fk = '$topic')";
$result_topics = mysql_query($query_topics, $connection) or die(mysql_error());
Create a unique index on the table and use insert ignore or on duplicate key update:
create unique index specimen_topic_lookup(specimen_fk, topic_fk);
insert ignore into specimen_topic_lookup(specimen_fk, topic_fk)
select $speciment_fk, $topic_fk;
Or, alternatively, you can just do the following without the unique index:
insert into specimen_topic_lookup(specifmen_fk, topic_fk)
select $speciment_fk, $topic_fk
from dual
where not exists (select 1
from specimen_topic_lookup
where specimen_fk = $specimen_fk and topic_fk = $topic_fk
);
Use an INSERT IGNORE statement. This will insert any rows that do not violate the unique key, and ignore the ones that do.
I am having a few difficulties with mysql and PDO.
I wish to insert a product into the database, however the product table contains foreign keys. Naturally, I will not know the Foreign key ID when inserting. Am I doing this right??? Is there a better way of tackling this problem?
TABLE Products
Id PK AI int
Name Varchar(20)
CategoryId Int FK
TypeId Int FK
TABLE Categories
Id Int PK
Cat varchar(20)
TABLE Types
Id Int PK
Type varchar(20)
$type = 'Gloves';
$category = 'Clothing';
$sql = 'INSERT INTO Products
SET Name = :name, CategoryId = :catId, TypeId = :typeId
WHERE
CategoryId IN (SELECT Id FROM Categories WHERE Cat = :category) AND
TypeId IN (SELECT Id FROM Types WHERE Type = :type)'
$stmt = $db->prepare($sql);
$stmt->execute(array(':name' => 'Pink childrens gloves', ':category' => $category, ':type' => $type));
As mentioned in a comment below: Normally, I would be getting the ID from a select box. I cannot do this because it will be a script executing the query, not a user.
are you sure that this is what you want?
$sql = 'INSERT INTO Products
SET Name = :name
WHERE
CategoryId IN (SELECT Id FROM Categories WHERE Cat = :category) AND
TypeId IN (SELECT Id FROM Types WHERE Type = :type)'
I think you are trying to use UPDATE
$sql = 'UPDATE Products
SET Name = :name
WHERE
CategoryId IN (SELECT Id FROM Categories WHERE Cat = :category) AND
TypeId IN (SELECT Id FROM Types WHERE Type = :type)'
MySQL allows a combination of SELECT + INSERT in a single query:
INSERT INTO tbl_temp2 (fld_id)
SELECT tbl_temp1.fld_order_id
FROM tbl_temp1 WHERE tbl_temp1.fld_order_id > 100;
... but I wouldn't care about it. You cannot do proper error checking if you do three different things in a single query.
My advice is that you first validate that there're a category and a type that match the given names. In that step, you can easily get the corresponding IDs, which will let you perform a simple INSERT. Additionally, if you need to insert many products, you can validate first and once.
In addition to #Álvaro G. Vicario's answer, you can also do something like (works in normal sql, I have not tried it with bound variables):
$sql = 'INSERT INTO Products
SET Name = :name,
CategoryId = (SELECT Id FROM Categories WHERE Cat = :category),
TypeId = (SELECT Id FROM Types WHERE Type = :type)';
But I would always check for existing categories and types first, insert where necessary and get the required id's as this will lead to unexpected results if there are no matches in the inner selects.
First of all you need to figure out which is the table that have foreign key data.
Then you need to get all possible values from foreign key table.
Finally you need to build and drop-down list or similar to give ability of select acceptable foreign key.
$q=$db->prepare('SELECT ke.referenced_table_name assoc_table,
ke.referenced_column_name assoc_col FROM
information_schema.KEY_COLUMN_USAGE ke WHERE ke.referenced_table_name IS NOT NULL
AND ke.table_schema=:database AND ke.table_name=:tablename AND ke.column_name=:col');
$q->bindValue(':database','mydatabasename'); //Set your database name here
$q->bindValue(':tablename','Departments'); //Set your table name here
$q->bindValue(':col','City'); //Set the column which foreign key values you want to have here
if($q->execute()) {
$foreingtable=$q->fetch(PDO::FETCH_ASSOC);
$q=$db->prepare('SELECT '.$foreingtable['assoc_col'].' FROM '.$foreingtable['assoc_table']);
if($q->execute())
echo json_encode($q->fetchAll(PDO::FETCH_COLUMN));
}
else {
header('http/1.1 500 Internal Server Error');
print_r($q->errorInfo());
exit;
}
More about this: Get list of possible foreign key values in MySql using PDO
What is the proper way to automatically create new tables based on values in another table? For instance, If table A has a column named city that contains different city values then I would need to create a new table based on each different city. Then all records with the respective city needs to be inserted into it's respective table. Also, if the city contains a space in the name it needs to be replaced with a an underscore. How could the same be done in MySQL?
In MS ACCESS I could accomplish this by:
Using A Select And Replace Query Named SELREP
SELECT table_A.column1, table_A.column2, table_A.city, Replace([city]," ","_") AS table_name_column FROM table_A;
Create a Public Function MakeTableCity
Public Function MakeTableCity()
DoCmd.SetWarnings False
Dim db As Database
Set db = Application.CurrentDb
Dim distinctValues As DAO.Recordset
Set distinctValues = db.OpenRecordset("SELECT table_name_column FROM SELREP GROUP BY table_name_column", dbOpenSnapshot)
Do Until distinctValues.EOF
DoCmd.RunSQL "SELECT * INTO " & distinctValues("table_name_column") & " FROM SELREP WHERE table_name_column ='" & distinctValues("table_name_column") & "'"
distinctValues.MoveNext
Loop
DoCmd.SetWarnings True
Set distinctValues = Nothing
Set db = Nothing
End Function
If you are planning to create a new
table city with data about cities,
meaning one row for every city, then
go ahead and read the answer.
If , on the other hand, you are
planning to make a new table for
every city, with identical columns,
then your plan is very bad design.
Read about normalization first.
First alternative is to create a table named city with fields that you want. Example:
CREATE TABLE city
( id INT auto_increment PRIMARY KEY
, name VARCHAR(50) NOT NULL
, population INT
, state CHAR(2)
) ;
Then copy the different city names into it with:
INSERT INTO city (name)
( SELECT DINSTINCT city --- change "city" into REPLACE(city, ' ', '_')
FROM table_A --- for the small changes you want
) ;
Then, update the other fields (population, state, etc).
If no two cities have same name, the JOINs between the two tables can then be done using ON table_A.city = city.name
If not, (and better anyway as the Primary Key of city will be smaller), you may ALTER the structure of table table_A by adding a field cityid and dropping the city field. Then the JOINs between the two tables will be done using ON table_A.cityid = city.id
Second option is to directly create table city with:
CREATE TABLE city AS
( SELECT DINSTINCT city AS name --- change "city" into REPLACE(city, ' ', '_')
FROM table_A --- for the small changes you want
) ;
and then alter the table defining Primary Key, adding (population, state, etc).