SQL subquery across tables with different column names - mysql

Say I have two tables: orderinfo and customerinfo
They look like this:
orderinfo:
OrderID
CustomerID
OrderCustomerName
OrderCustomerAddress
customerinfo:
CustomerID
CustomerName
CustomerAddress
Both OrderID and CustomerID are unique. When an order is to be created, I need to copy the info from customerinfo to orderinfo at that time as the customer may change his information later.
I hope I can just use CustomerID to achieve this. How should I write the subquery? Thanks.

When you insert a new order, you can grab a "snapshot" of the customer data with the following simple INSERT... SELECT query syntax:
INSERT INTO orderinfo (CustomerID, OrderCustomerName, OrderCustomerAddress, status)
SELECT CustomerID, CustomerName, CustomerAddress, 'P'
FROM customerinfo
WHERE CustomerID = <customerid here>
This inserts a new order with the customer info at the exact time the order was placed.
This information will remain just as how it was inserted even if the customer updates his or her information in the customerinfo table because there aren't any cascade effects other than if the CustomerID changes (which it shouldn't, and even if it did change, it wouldn't change the "snapshot" data).

To update orderinfo with the current values for customer name and address based on a CustomerID, use an UPDATE JOIN:
UPDATE
orderinfo
JOIN customerinfo ON orderinfo.CustomerID = customerinfo.CustomerID
SET
OrderCustomerName = CustomerName,
OrderCustomerAddress = CustomerAddress
WHERE OrderID = <some_order_id>
This is useful if you want to "freeze" the customer information associated with a specific order, even if the customer later changes that information globally. If I understand correctly, that is your intent.
You can also do this with an AFTER INSERT trigger on the orderinfo table. Then you needn't execute a separate query:
CREATE TRIGGER update_order_cust_info AFTER INSERT ON orderinfo
FOR EACH ROW
BEGIN
UPDATE
orderinfo
JOIN customerinfo ON orderinfo.CustomerID = customerinfo.CustomerID
SET
NEW.OrderCustomerName = CustomerName,
NEW.OrderCustomerAddress = CustomerAddress
WHERE OrderID = NEW.OrderID
END

What you are looking for is
INSERT INTO orderinfo
(OrderID, CustomerID, OrderCustomerName, OrderCustomerAddress)
VALUES (
'12345',
'789',
(SELECT CustomerName FROM customerinfo WHERE CustomerID = '789'),
(SELECT CustomerAddress FROM customerinfo WHERE CustomerID = '789')
)
But i strongly advise a change in your database structure. What you try to do violates the Database Normalisation rules. Instead, read this article on Wikipedia on Slowly Changing Dimensions and implement it as such.

Related

Why I can't directly use the column from another table behind not in function?

I have two tables. One is customers and another one is orders. This is the raw data to create the two tables:
Create table If Not Exists Customers (Id int, Name varchar(255));
Create table If Not Exists Orders (Id int, CustomerId int);
insert into Customers (Id, Name) values ('1', 'Joe');
insert into Customers (Id, Name) values ('2', 'Henry');
insert into Customers (Id, Name) values ('3', 'Sam');
insert into Customers (Id, Name) values ('4', 'Max');
insert into Orders (Id, CustomerId) values ('1', '3');
insert into Orders (Id, CustomerId) values ('2', '1');
And now I have to write a SQL query to find all customers who never order anything.
This is the correct answer:
select
name as customer
from
customers
where
customers.id
not in
(select
customerid
from
orders);
This is my answer:
select
name as customer
from
customers
where
customers.id
not in
orders.customerid;
And the MySQL response to my codes is "Error Code: 1064. You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'orders.customerid' at line 8".
What's wrong with my codes?
Your code is missing the reference to the orders table. This ref for a valid SQL syntax must be declared in proper FROM and WHERE clause or in a proper JOIN clause
For "not in" you could use left join and check for null
select name as customer
from customers
left join orders on orders.customerid = customer.id
where orders.customerid is null
If you don't declare the reference to the table orders simply the db engine can't know from which table must be retrieved the column value. The table declaration must be explicit and assigned in the proper clause (from or join).
IN, which is not a function BTW, needs a "set" or "list" as second operator which can typically be obtained by a subquery (or by giving a bunch of comma separated literals). In your case the subquery needs to get all customer IDs from the orders. So you might try:
SELECT c.name AS customer
FROM customers c
WHERE c.id NOT IN (SELECT o.customerid
FROM orders o);
But instead of using NOT IN, NOT EXISTS and a correlated subquery might be a better, possibly more performant solution here, especially if an index on (orders.customerid) exists:
SELECT c.name AS customer
FROM customers c
WHERE NOT EXISTS (SELECT *
FROM orders o
WHERE o.customerid = c.id);
You ask "why" about a language that's almost a half-century old. With respect, some of those language-design decisions are hard to recover. To get a perfect answer you need to track down Codd, Chamberlin, and Boyce. Why ask why?
At a more abstract level: SQL is fundamentally a set-manipulation language. Tables contain sets of rows. SELECT queries make their own sets -- they're even called result sets -- from other sets of rows.
In the SQL world, a column is not a set by itself. You can make a set from a column by saying things like this.
SELECT customerid FROM orders
And, the NOT IN() operation takes a set with just one column as its parameter. So you can give NOT IN() and IN() sets but not columns.

Insert records from one table to multiple relational tables using MySQL

I have three tables customers, customer_entity, customer_info. I wanted to insert records from customers table to customer_entity and customer_info at same time, but customer_entity tables primary key will be part of customer_info table.
Assumed Code can we write something like this?
INSERT INTO customer_entity (mobile, name)
INSERT INTO customer_info (customer_entity_id,email, name)
SELECT mobile, name, email customers FROM customers
I dont want to use any programming language only MYSQL
This query may help you.
INSERT INTO customer_entity (mobile,email)
SELECT mobile, email FROM customer ORDER BY id ASC;
INSERT INTO customer_info (customer_entity_id, email)
SELECT customer_entity.id, email, (SELECT email FROM customer WHERE mobile= customer_entity.mobile) FROM customer;
Here the customer table is the main table which has data already and we are inserting it into customer_entity table and customer_info table with customer_entity_id.
You can try using SELECT LAST_INSERT_ID() also:
INSERT INTO customer_entity (mobile,email)
INSERT INTO customer_info (LAST_INSERT_ID(), email)
Docs
If you insert a record into a table that contains an AUTO_INCREMENT
column, you can obtain the value stored into that column by calling
the mysql_insert_id() function.

Appending data from another database

I'm trying to add an age column to a database (junecustomers) using a DOB column from an existing database (customerdata).
I have a matching firstname, lastname, and customerid column in both data sets. In the customerdata data set, I have the DOB column that I want to use to calculate the age. Is this correct?
select * from junecustomers
left join customerdata
on junecustomers.customerid = customerdata.customerid
alter table (junecustomers)
add select
trunc(months_between(sysdate, dob)/12) Age from customerdata
Note: the customerdata database has multiple entries for some of the customers but the junecustomers database has only one entry per customer.
You need to run two scripts, one to add the column and one to update the data, e.g.:
ALTER TABLE junecustomers
ADD COLUMN age INT;
UPDATE junecustomers jc
SET jc.age = (SELECT trunc(months_between(sysdate, dob)/12) FROM customerdata
WHERE customerid = jc.customerid LIMIT 1);

Use of NOT EXISTS in SQL queries

I have read all most of the answers and even tried them. But this is my case where I'm inserting record from a .csv file into the database. I want to insert a record if it does not exist.
Here is my query
INSERT INTO retailer(retailerCode, contact, shopName, address,
retailerType, lastVisit, officeID, createDate)
VALUES ('', '$emapData[1]', '$emapData[2]', '$emapData[3]',
'$emapData[4]', '', '$id', CURDATE())
WHERE NOT EXISTS (SELECT retailerID
FROM retailer
WHERE contact = '$emapData[1]')
Here $emapData is a PHP array that saves the records of the .csv file. The insert statement works fine without this part
WHERE NOT EXISTS (SELECT retailerID
FROM retailer
WHERE contact = '$emapData[1]')
But my goal is not achieved.
You cannot use WHERE with INSERT in that way.
What you can do:
As #Sylwit suggested, create unique index on contact and use INSERT IGNORE
alter table retailer add unique (contact)
Use INSERT INTO SELECT syntax
INSERT INTO retailer(retailerCode,contact,shopName,address,retailerType,lastVisit,officeID,createDate)
select '','$emapData[1]','$emapData[2]','$emapData[3]','$emapData[4]','','$id', CURDATE()
from retailer
WHERE NOT EXISTS (SELECT retailerID FROM retailer WHERE contact='$emapData[1]')

double select within a insert sql

I have two tables:
Persons (PersonID, LastName, FirstName)
Orders (O_Id, OrderNo, P_Id)
Orders.P_Id should have values from Persons.PersonID.
I am trying to do an insert on Orders to insert the P_Id into orders but I want it to match a value in my Persons table and also my Orders table so I can link them up and allow the P_id to be linked to the order.
I have tried this below? not sure what the best way to do this would be?
INSERT INTO Orders (P_Id)
SELECT PersonID FROM Persons
WHERE PersonID='1'
UNION ALL
SELECT O_Id FROM Orders
WHERE O_Id ='1';
Edit:
I have tried UNION ALL but it doesn't add them on the same line here is my sql fiddle which shows what is happening:
http://sqlfiddle.com/#!9/30a71/1
anyone help?
I think you want to UPDATE (not INSERT):
UPDATE ORDERS SET
P_Id = 1
WHERE O_Id = 1;
See SQLFiddle
I think what you're actually trying to achieve is not through the form of an INSERT, but an UPDATE:
Try this:
UPDATE Orders
SET P_ID = (SELECT PersonID From Persons WHERE PersonID = 1)
WHERE O_ID = 1
You can't "INSERT" a value into an existing row with a INSERT command. For that reason the UPDATE command exists.
The INSERT is useful only when creating a row in the table. For all other scenarios, when you are trying to populate a column with data, into an existing row, then you should use UPDATE.
The above query is basically a re-write of what you have, but since you already know the value of PersonID which you want to populate the P_ID column with, then you can simplify the query to:
UPDATE Orders
SET P_Id = 1
WHERE O_Id = 1