Insert into table but unsure of foreign key - mysql

Table Order
oid payerName address
1 james 1 brown
2 smith 2 smith
Table order_item
oid type price
1 AN94 3000
2 AK47 1000
order_item as a foreign from Order.
oid is an auto increment in Order Table
but in order_item table it is not(dont know if thats is the right way to do it)
I have an insert statement which inserts into both tables at the same time. I was wonder if it the right to make order_item oid an auto increment as well? because the is not other way I can make it copy the oid from order table.
What is the best approach to this small issue.

You shouldn't make order id field in order_items table to be auto_increment. To obtain a value of auto generated id when you insert a row in orders table use LAST_INSERT_ID() function.
It's OK to have it's own auto_increment id column in order_items. Sometimes it comes very handy (e.g. when you want to update an individual order_item row you can reference it by its own id rather than by a combination of columns).
That being said proposed schema might look like
CREATE TABLE orders
(`order_id` int not null auto_increment primary key,
`payerName` varchar(5),
`address` varchar(8)
);
CREATE TABLE order_items
(`order_item_id` int not null auto_increment primary key,
`order_id` int,
`type` varchar(4),
`price` decimal(19,2),
foreign key (order_id) references orders (order_id)
);
Names of id columns has been intentionally renamed in my example for clarity. You don't have to change yours obviously.
Now to insert an order and an order item you do
INSERT INTO orders (`payerName`, `address`)
VALUES ('james', '1 brown');
INSERT INTO order_items (`order_id`, `type`, `price`)
VALUES (LAST_INSERT_ID(), 'AN94', 3000);
Here is SQLFiddle demo
You didn't mention PDO in your question but your code using it might look something like this
try {
$db = new PDO('mysql:host=localhost;dbname=test', 'user', 'userpwd');
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$db->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
//$db->beginTransaction();
$query=$db->prepare("INSERT INTO orders (`payerName`, `address`) VALUES (?, ?)");
$query->execute(array('james', '1 brown'));
$order_id = $db->lastInsertId();
$query=$db->prepare("INSERT INTO order_items (`order_id`, `type`, `price`) VALUES (?, ?, ?)");
$query->execute(array($order_id, 'AN94', 3000));
//$db->commit();
} catch (PDOException $e) {
echo "Exeption: " .$e->getMessage();
}
$query = null;
$db = null;

Since order_item->oid depends on order->oid, Order should exist first before the order_item
This one is right because the oid(1) exist in the order table
insert into order_item (oid,type,price) values('1','AN94','3000')
This one is wrong because the oid(3) doesn't exist in the order table
insert into order_item (oid,type,price)values('3','sample','3333')
BTW the table order-oid should be a primary key

Orders
order_id(auto inc), payer_name, address
Order Items
order_item_id(auto inc), order_id, item_id, type, quantity, unit_price
Product
item_id(auto inc), item_name, item_unit_price, item_group

Related

How do you ON DUPLICATE KEY UPDATE a user info without breaking the database?

I have a simple table with students in MySQL database. Similar to:
student_id
student_name
teacher_id
1
Adam
100
2
Bob
100
3
Carl
100
4
Dan
200
Teachers can input new students or change existing student's names. Currently I have this set up to update via:
INSERT INTO student_list (name) VALUES (:name) ON DUPLICATE KEY UPDATE student name= values(name)
The problem is that this completely breaks any associated student_ids in other tables (as the ON DUPLICATE KEY UPDATE changes the student_id). I could look up each entry before updating, but it seems messy. Surely there's a better way.
CREATE TABLE `sabrep_db`.`students` ( `student_id` INT NOT NULL AUTO_INCREMENT , `name` TEXT NOT NULL , `teacher_id` INT NOT NULL , PRIMARY KEY (`student_id`)) ENGINE = InnoDB;
INSERT INTO `students` (`student_id`, `name`, `teacher_id`) VALUES (NULL, 'Adam', '100'), (NULL, 'Bob', '100');
Should also pass a key (usually the primary key) as part of the insert so that it knows the key reference to update when there is a duplicate, name is passed twice for the update portion:
INSERT INTO student_list (student_id, name)
VALUES (:student_id, :name) ON DUPLICATE KEY UPDATE student_name = :name;

Primary / foreign key; joining multiple tables using subqueries

I have a question for an assignment with 5 tables as shown below. I need to write a query with the minimum cost for each sport:
2nd column is equipment_name:
I think I need to do a bunch of joins in subqueries with the primary keys being the id columns and the foreign keys the name_id columns. Is this the right approach?
You don't need a bunch of joins; minimally this question can be solved by one join between the store_equipment_price and the sports_equipment tables - if these two are joined on equipment id then you'll effectively get rows that can give the cost of starting up in each sport per store. You'll need to group by the sport id and the store id; don't forget that it might be cheaper to start soccer by getting all the gear from store A but it might be cheaper to start golf by going to tore B - tho I how I read the question. If however you're prepared to get your gloves from store A and your bat from store B etc then we don't even group by the store when summing, instead we work out which store is cheapest for each component rather than which store is cheapest for each sport overall.
If you're after producing named stores/sports on your result rows then you'll need more joins but try getting the results right based on the fewest number of joins possible to start with
Both these queries will ultimately be made a lot easier by the use of an analytic/windowing function but these are database dependent; never post an sql question up without stating what your db vendor is, as there are few questions that are pure ISO SQL
You question is not completely clear, I assume you need to find stores from which to buy each equipment for all sports so as to incur minimum expense. Following query will achieve this
select s.sports, e.equipment_name, min(sep.price),
(select store_name from stores st where st.id = sep.store_id) store_name
from sports s
join sports_equipment se on s.id = se.sport_id
join equipment e on e.id = se.equipment_id
join sports_equipment_prices sep on sep.equipment_id = se.equipment_id
group by s.sports, e.equipment_name
order by s.sports, e.equipment_name
;
Following 'create table' and 'insert data' script are based on your screen images
create table sports (
id INTEGER PRIMARY KEY AUTOINCREMENT,
sports varchar(50)
);
insert into sports(sports) values('golf');
insert into sports(sports) values('baseball');
insert into sports(sports) values('soccer');
create table stores (
id INTEGER PRIMARY KEY AUTOINCREMENT,
store_name varchar(50)
);
insert into stores(store_name) values('A');
insert into stores(store_name) values('B');
insert into stores(store_name) values('C');
create table equipment (
id INTEGER PRIMARY KEY AUTOINCREMENT,
equipment_name varchar(50)
);
insert into equipment(equipment_name) values('shoes');
insert into equipment(equipment_name) values('ball');
insert into equipment(equipment_name) values('clubs');
insert into equipment(equipment_name) values('glove');
insert into equipment(equipment_name) values('bat');
create table sports_equipment (
sport_id INTEGER not null,
equipment_id INTEGER not null,
FOREIGN KEY(sport_id) REFERENCES sports(id),
FOREIGN KEY(equipment_id) REFERENCES equipment(id)
);
insert into sports_equipment values(1, 1);
insert into sports_equipment values(1, 2);
insert into sports_equipment values(1, 3);
insert into sports_equipment values(2, 2);
insert into sports_equipment values(2, 4);
insert into sports_equipment values(2, 5);
insert into sports_equipment values(3, 1);
insert into sports_equipment values(3, 2);
create table sports_equipment_prices (
id INTEGER PRIMARY KEY AUTOINCREMENT,
store_id INTEGER not null,
equipment_id INTEGER not null,
price INTEGER not null,
FOREIGN KEY(store_id) REFERENCES stores(id),
FOREIGN KEY(equipment_id) REFERENCES equipment(id)
);

ON DUPLICATE KEY UPDATE with WHERE

I've got an INSERT ... ON DUPLICATE KEY UPDATE query and I am trying to add WHERE clause to it:
INSERT INTO `product_description` (
`product_id`,`language_id`,`name`,
`description`,`meta_description`,
`meta_keyword`,`tag`
) VALUES (
$getProductId, $languageId, '$pName', '$pDescription', '', '', ''
)
ON DUPLICATE KEY UPDATE
`name` = '$pName',
`description` = '$pDescription'
I want to restrict the UPDATE to those 2 conditions:
WHERE `model` = 'specific-model' AND `sku` NOT LIKE '%B15%'
If I add this part of query to the end of the original query I get a MySQL syntax error. What would be a working solution?
Update: Please note that model and sku are in another table, and the common key is product_id
I would suggest you to use some sort of prepared statement instead of concatenating strings, so you should do something like this:
INSERT INTO `product_description` (
`product_id`, `language_id`, `name`,
`description`, `meta_description`,
`meta_keyword`, `tag`
) VALUES (?, ?, ?, ?,'','','')
but this is not part of the question.
I was thinking of answering with a simple CASE WHEN but the challenging part of your question is that the restrict conditions are not in the product_description table but are from another table. So I think we can just use a TRIGGER:
CREATE TRIGGER product_description_upd
BEFORE UPDATE ON product_description
FOR EACH ROW
IF NOT EXISTS(SELECT * FROM models
WHERE product_id=new.product_id
AND model='Abc' AND `sku` NOT LIKE '%B15%') THEN
SET new.name=old.name;
SET new.description=old.description;
END IF;
//
then you can use an INSERT query like:
INSERT INTO `product_description` (col1, col2, ...)
VALUES (..., ..., ...)
ON DUPLICATE KEY
UPDATE name=VALUE(name),description=VALUE(description)
Please see a fiddle here.
The only thing to note here is that even a standard UPDATE query will be affected.
CREATE TABLE product_description (
product_id INT PRIMARY KEY,
name VARCHAR(100),
description VARCHAR(100)
);
CREATE TABLE models (
product_id INT,
model VARCHAR(100),
sku VARCHAR(100)
);
INSERT INTO models VALUES
(1, "Abc", "ZZZ"),
(2, "Abc", "B15");
INSERT INTO product_description VALUES
(1, "Car", "Red"),
(2, "Truck", "Pink");
INSERT INTO `product_description` VALUES (1, "NewCar", "DeepRed")
ON DUPLICATE KEY UPDATE name=VALUES(name), description=VALUES(description);
Assuming, product_id must be in models.
INSERT INTO `product_description` (product_id, name, description)
SELECT models.product_id, "SuperCar" as name, "DarkRed" as description
FROM `models` WHERE model="Abc" AND `sku` NOT LIKE "%B15%"
ON DUPLICATE KEY UPDATE name="UpdatedCar", description="UpdatedRed";
refer to http://sqlfiddle.com/#!9/69624e/1
Hopefully this solves the problem. You can play with SELECT query for different result.

insert child mysql

I've got a newbie question...
I've got two tables:
parentTable
-----------
id_user int(11) not null auto increment primary key,
email varchar(64),
pass varchar(64)
childTable
----------
id_user int(11) not null,
name varchar(64),
address varchar(512),
foreign key (id_user) references parentTable(id_user)
on update cascade
on delete restrict
Now can I insert:
insert into childTable (id_user) select id_user from parentTable where id_user = '1'
But I just want to insert also name and address values.
Sorry for the newbie question, but I lurked for a day and found nothing.
Thank you in advance for your reply.
The interesting part about your query is that you know the id_user you're trying to select to insert - it's in your WHERE clause.
If you will always know the id_user, you can skip the extra SELECT portion of the query and directly do:
INSERT INTO childTable (id_user, name, address) VALUES (1, 'some name', '123 test street');
If you, for some other reason, need the additional SELECT, you can append the name/address values directly into the field-list, like this:
INSERT INTO childTable (id_user, name, address)
SELECT id_user, 'some name', '123 test street' FROM parentTable WHERE id_user = '1';

Insert and set value with max()+1 problems

I am trying to insert a new row and set the customer_id with max()+1. The reason for this is the table already has a auto_increatment on another column named id and the table will have multiple rows with the same customer_id.
With this:
INSERT INTO customers
( customer_id, firstname, surname )
VALUES
((SELECT MAX( customer_id ) FROM customers) +1, 'jim', 'sock')
...I keep getting the following error:
#1093 - You can't specify target table 'customers' for update in FROM clause
Also how would I stop 2 different customers being added at the same time and not having the same customer_id?
You can use the INSERT ... SELECT statement to get the MAX()+1 value and insert at the same time:
INSERT INTO
customers( customer_id, firstname, surname )
SELECT MAX( customer_id ) + 1, 'jim', 'sock' FROM customers;
Note: You need to drop the VALUES from your INSERT and make sure the SELECT selected fields match the INSERT declared fields.
Correct, you can not modify and select from the same table in the same query. You would have to perform the above in two separate queries.
The best way is to use a transaction but if your not using innodb tables then next best is locking the tables and then performing your queries. So:
Lock tables customers write;
$max = SELECT MAX( customer_id ) FROM customers;
Grab the max id and then perform the insert
INSERT INTO customers( customer_id, firstname, surname )
VALUES ($max+1 , 'jim', 'sock')
unlock tables;
Use alias name for the inner query like this
INSERT INTO customers
( customer_id, firstname, surname )
VALUES
((SELECT MAX( customer_id )+1 FROM customers cust), 'sharath', 'rock')
SELECT MAX(col) +1 is not safe -- it does not ensure that you aren't inserting more than one customer with the same customer_id value, regardless if selecting from the same table or any others. The proper way to ensure a unique integer value is assigned on insertion into your table in MySQL is to use AUTO_INCREMENT. The ANSI standard is to use sequences, but MySQL doesn't support them. An AUTO_INCREMENT column can only be defined in the CREATE TABLE statement:
CREATE TABLE `customers` (
`customer_id` int(11) NOT NULL AUTO_INCREMENT,
`firstname` varchar(45) DEFAULT NULL,
`surname` varchar(45) DEFAULT NULL,
PRIMARY KEY (`customer_id`)
)
That said, this worked fine for me on 5.1.49:
CREATE TABLE `customers` (
`customer_id` int(11) NOT NULL DEFAULT '0',
`firstname` varchar(45) DEFAULT NULL,
`surname` varchar(45) DEFAULT NULL,
PRIMARY KEY (`customer_id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1$$
INSERT INTO customers VALUES (1, 'a', 'b');
INSERT INTO customers
SELECT MAX(customer_id) + 1, 'jim', 'sock'
FROM CUSTOMERS;
insert into table1(id1) select (max(id1)+1) from table1;
Use table alias in subquery:
INSERT INTO customers
( customer_id, firstname, surname )
VALUES
((SELECT MAX( customer_id ) FROM customers C) +1, 'jim', 'sock')
None of the about answers works for my case. I got the answer from here, and my SQL is:
INSERT INTO product (id, catalog_id, status_id, name, measure_unit_id, description, create_time)
VALUES (
(SELECT id FROM (SELECT COALESCE(MAX(id),0)+1 AS id FROM product) AS temp),
(SELECT id FROM product_catalog WHERE name="AppSys1"),
(SELECT id FROM product_status WHERE name ="active"),
"prod_name_x",
(SELECT id FROM measure_unit WHERE name ="unit"),
"prod_description_y",
UNIX_TIMESTAMP(NOW())
)
Your sub-query is just incomplete, that's all. See the query below with my additions:
INSERT INTO customers ( customer_id, firstname, surname )
VALUES ((SELECT MAX( customer_id ) FROM customers) +1), 'jim', 'sock')
You can't do it in a single query, but you could do it within a transaction. Do the initial MAX() select and lock the table, then do the insert. The transaction ensures that nothing will interrupt the two queries, and the lock ensures that nothing else can try doing the same thing elsewhere at the same time.
We declare a variable 'a'
SET **#a** = (SELECT MAX( customer_id ) FROM customers) +1;
INSERT INTO customers
( customer_id, firstname, surname )
VALUES
(**#a**, 'jim', 'sock')
This is select come insert sequel.
I am trying to get serial_no maximum +1 value and its giving correct value.
SELECT MAX(serial_no)+1 into #var FROM sample.kettle;
Insert into kettle(serial_no,name,age,salary) values (#var,'aaa',23,2000);