MySql: Referencing columns from main query in subquery - mysql

the following table is supposed to reflect the storage location (stockId) of
purchases, where a purchase is received in parts at different times. (I omitted the date column.)
CREATE TABLE stock (purchaseNo INT NOT NULL, quantity INT NOT NULL, stockId INT NOT NULL)
Let's say purchase 10 has arrived in three parts and is stored at two different locations (5 and 6).
INSERT INTO stock VALUES(10, 2000, 5)
INSERT INTO stock VALUES(10, 3000, 5)
INSERT INTO stock VALUES(10, 1000, 6)
Now I would like to select those stocks having more than 2000 kg (quantity) available. This can be done with
SELECT stockId AS id, SUM(quantity) AS q
FROM stock
WHERE purchaseNo=10
GROUP BY stockId
HAVING q>2000
But since I am only interested in the stockId, I would like to prevent the quantity to be returned as well. Is this possible?
One of my failing attempts is
SELECT stockId AS id
FROM stock
WHERE purchaseNo=10 AND (SELECT SUM(quantity) FROM stock WHERE purchaseNo=10 AND stockId=id) >2000
with or without GROUP BY the following error is returned:
ERROR 1054 (42S22): Unknown column 'id' in 'where clause'

You could pass the SUM to HAVING and skip it from the results.
SELECT stockId AS id
FROM stock
WHERE purchaseNo=10
GROUP BY stockId
HAVING SUM(quantity)>2000;
SQLFiddle example.

Have you tried something like this?
SELECT distinct S1.stockId AS S1.id from stock S1
inner join(
SELECT S2.stockId AS S2.id, SUM(S2.quantity) AS S2.q
FROM stock S2
WHERE S2.purchaseNo=10
GROUP BY S2.stockId
HAVING S2.q>2000)
on S1.stockId=S2.stockId

Related

Getting error in MySQL as Error Code: 1054. Unknown column 'Price' in 'having clause'

I have created a table as follows:
create table products
(productID int not null auto_increment,
Name varchar(30),
Price float ,
CoffeOrigin varchar(30),
primary key (productID));
Now the question is "Display the name of products whose price is less than average price."
My 1st attempt:
select Name from products having Price <= avg(Price);
It is giving me error as Error Code: 1054. Unknown column 'Price' in 'having clause'
My 2nd attempt:
select * from products having Price <= avg(Price);
It is giving me incomplete output. Here average Price is 3.14 but is only products with price less than 3 are being displayed in the result.
Try
Select NAME from products
Group by Products, Price
Having price <= AVG(Price)
The columns must be included in a group by in order to be correctly selected
You want to compare the price on each row to the average price over the whole table, so you can't just use aggregation. Both your attempts are not valid aggregation queries.
If you are running MySQL 8.0, you can just use window functions:
select p.*
from (select p.*, avg(price) over() avg_price from products p) p
where price <= avg_price
In earlier versions, one option uses a scalar subquery to compute the average:
select p.*
from products p
where price <= (select avg(price) from products)

Create a Stored Procedure Debug

Using the data and schema from this site: Using the data from this site: https://www.sqlservertutorial.net/sql-server-sample-database/
I am given the prompt:
Create a stored procedure called placeOrder() that can be called to insert a new order in the database. It will receive a customerId as an INT, a productId as an INT and a qty as an INT and return (as anoutput parameter) the order_id of the new row created in table orders.
This stored procedure will find the store with the largest stock of that particular product and assign that store to the order. The order_status should be set to 1 (i.e. Pending), the current system date (see function CURDATE) will be assigned to order_date, column required_date will be 7 days from the current system date (see function ADDDATE) and the column staff_id will be assigned for anyone that works in the selected store (per previous requirement). Since the order_id column is not an auto-incremented column you need to calculate the value for it. You can use max(order_id) to find out the highest order_id in the table.
The order_item row shall be set to the productId and qty passed to the stored procedure. The item_id shall be set to 1 (since this order will only have one item).The list price should be retrieved from the products table using the passed productId. The discount value should be set to 0
If I'm understanding the prompt correctly, I need to first the store id which has the most of a particular product. Once I have that store, I need to insert a new row of data into the table "orders" with the essential data being order_id, customer_id, order_status, order_date, required, date, and staff_id. I do NOT understand what the last part of the question is asking/how to go about solving.
Here's my current code, but I'm almost positive it's chalk full of errors and notes and missing pieces so please help me out where you can:
DELIMITER //
CREATE procedure placeOrder (IN customerID INT, IN productID INT, IN QTY INT, OUT order_id INT)
BEGIN
DECLARE customerID INT;
DECLARE produtcID INT;
DECLARE quantity INT;
SELECT customer_id INTO customerID from customers where customer_id = customerID;
SELECT product_id INTO productID from order_items where product_id = productID;
SELECT quantity INTO qty from order_items where quantity = qty;
/**find store id with max stocks of product**/
select st.store_name, sk.store_id from stocks as sk
INNER JOIN
stores as st
ON sk.store_id = st.store_id
WHERE max(sk.quantity)
GROUP by sk.product_id;
select st.store_id from stores as st
INNER JOIN orders as o
ON st.store_id= o.store_id
Insert into orders (order_id, customer_id, order_status, order_date, required_date, staff_id)
WHERE order_status = '1',
AND order_date = select(curdate()),
AND required_date = adddate('order_date' +7),
AND staff_id = /**ANYONE from store listed in precious query (how do I relate these two queries)**
END
Don't re-declare function parameters.
You don't need to use a SELECT query to set variables that are already set in parameters.
You're not getting the store with the maximum quantity correctly. Use ORDER BY sk.quantity DESC LIMIT 1
You need to use INTO <variable> in a query to set variables from a query. If you don't do this, the result of the query will be turned into the result of the procedure, which isn't desired here.
You don't use WHERE in an INSERT statement. WHERE is used for finding existing rows that match a condition, but INSERT is for creating new rows. You use VALUES() to list all the values that should be assigned to the specified columns.
CREATE procedure placeOrder (IN customerID INT, IN productID INT, IN QTY INT, OUT orderId INT)
BEGIN
DECLARE topStoreId INT;
DECLARE staffId INT;
/**find store id with max stocks of product**/
select sk.store_id
from stocks as sk
INNER JOIN stores as st
ON sk.store_id = st.store_id
WHERE sk.product_id = productID
ORDER BY sk.quantity DESC
LIMIT 1
INTO topStoreId;
/* Pick an arbitrary staff from the selected store */
SELECT staff_id
FROM staffs
WHERE store_id = topStoreId
LIMIT 1
INTO staffId;
SELECT MAX(order_id)
FROM orders AS o
INTO orderId;
Insert into orders (order_id, customer_id, order_status, order_date, required_date, staff_id, store_id)
VALUES (orderId, customerId, 1, CURDATE(), DATE_ADD(CURDATE(), INTERVAL 1 WEEK), staffId, topStoreId);
END

MySQL "ambiguous field list" and daily average calc

CREATE TABLE table1(
City CHAR(10),
Sales INT NOT NULL,
Sale_date DATE NOT NULL,
First_order INT NOT NULL
);
INSERT INTO table1 VALUES
("Paris",2000,'2018-01-02',1),
("Munich",2000,'2018-02-17',1),
("Paris",2000,'2018-03-31',2),
("Paris",1000,'2017-01-01',2),
("Paris",1000,'2017-02-02',1),
("Paris",1000,'2017-03-02',1),
("Paris",10000,'2018-01-02',2),
("Munich",1000,'2017-01-01',1);
http://sqlfiddle.com/#!9/f0f0cb/2
New to SQL, I'm getting an error from this query "Column 'City' in field list is ambiguous" is it a problem with the alias / inner join?
Objectives:
Daily Average sales per city (on q1 2018)
YoY sales growth Q1_2017 vs Q1_2018
Daily Average number of new customers (New customers have a value of 1 in the column "First_order" and 2 for returning customers)
On that 3rd point particularly I am lost, what should I use to calculate this? Count function?
When you have more than one table in a query, qualify all column references. That is, show the table where they come from.
So:
SELECT t1.City, AVG(t1.Sales) as q1_daily_avg_Sales,
(SUM(t1.Sales) - tt1.q1_2017_Sales) / tt1.q1_2017_Sales AS Sales_vs_SPLY
FROM table1 t1 INNER JOIN
(SELECT t1.City, SUM(t1.Sales) AS q1_2017_Sales
FROM table1 t1
WHERE t1.Sale_date BETWEEN '2017-01-01' AND '2017-03-31'
GROUP BY t1.City
) tt1
ON t1.City = tt1.City
WHERE t1.Sale_date BETWEEN '2018-01-01' AND '2018-03-31'
GROUP BY t1.City;
Here is the SQL Fiddle.

MYSQL reference to another table fields

I am trying to get for a specific period of time the quantity of products sold. For this example : the products table has a quantity field which increments whenever a certain product is sold. The orders table has a start date and end date. Is there a way i can hold a reference of the products table (like an object) in my orders table so i can see the quantity sold for each product every start - end date ? A quick example : I am having 2 products:
1 bike 25000 3 and 2 sportbike 30000 5 sold for my date. So an order would be something like : 1 05.07.2015 05.07.2015 and those products.
CREATE TABLE products (
product_no integer PRIMARY KEY,
name varchar(20),
price float,
quantity integer,
);
CREATE TABLE sells (
sell_id integer PRIMARY KEY,
start_date date,
end_date date
);
//New idea :
CREATE TABLE products (
product_no integer PRIMARY KEY,
name varchar(20),
price float
);
CREATE TABLE sells (
sell_id integer PRIMARY KEY,
date date,
quantity integer,
product_id integer FOREIGN KEY
);
Unless an order is always for a single product, I think you will need a third table. To me a minimal *) data model for this situation would look like this:
CREATE TABLE products (
product_no integer PRIMARY KEY,
name varchar(20) not null,
price float,
quantity_on_stock integer not null
);
CREATE TABLE orders (
order_id integer PRIMARY KEY,
order_date date not null
);
CREATE TABLE orderlines (
order_id integer not null REFERENCES orders.order_id,
product_no integer REFERENCES products.product_no,
price integer,
quantity integer not null,
PRIMARY KEY(order_id, product_no)
);
Then, a query to get sales in a certain period could look like this:
select
p.product_no,
p.name,
sum(ol.quantity)
from
products p
inner join orderlines ol on ol.product_no = p.product_no
inner join orderlines o on o.order_id = ol.order_id
where
ol.order_date between :start_date and :end_date
*) I say minimal, but it's actually less than minimal. You'd probably want to store invoices too, or at least some kind of indication that says whether an order is actually payed for and delivered, since sometimes orders are cancelled, or are just waiting open.
You definitely want a sales table showing what was sold and when, rather than a table showing the running inventory.
It is much easier in SQL to aggregate detail to create running totals than it is to reconstruct detail from running totals.
So, if you happen to have a sales table containing, among other things, columns for product_id, quantity, and ts (the timestamp of the sale), then you can get a summary of sales by product_id and sales date like this.
SELECT product_id,
DATE(ts) AS salesdate,
SUM(quantity) AS quantity
FROM sales
WHERE ts >= :start_date
AND ts < :end_date + INTERVAL 1 DAY
GROUP BY DATE(ts), product_id
One of the things that's cool about this is you can add a column for transaction_type to this table. If a customer returns a product you tag it return and use a negative quantity. If your shop receives a shipment of products you tag it restock and use a negative quantity. Then, if you want to show net sales -- sales less returned products -- it's a small change to the query.
SELECT product_id,
DATE(ts) AS salesdate,
SUM(quantity) AS quantity
FROM sales
WHERE ts >= :start_date
AND ts < :end_date + INTERVAL 1 DAY
AND transaction_type IN ('sale', 'return')
GROUP BY DATE(ts), product_id
If you want to know how much stock you had on on hand for each product for a particular end_date, you do this (starting from the beginning of time).
SELECT product_id,
SUM(quantity) AS quantity
FROM sales
GROUP BY product_id
In actual inventory / sale systems, it's common to take inventory once a year, close out the previous year's sales table, and start a new one with a starting value for each product. it's also common to organize sales by sales_order and sales_detail, so you can track the sale of multiple items in each transaction.

Return the proportionate share of the same type

just for example lets say I have this table sales(ID,type,price).
Now, for each entry I have to return its proportion of its type sellings.
For instance, if my total revenue from selling bikes is 1000, and I sold 1 pair of bike for 100 so it's proportion will be 0.1.
How do I implement it over SQL?
Thanks!
Set up:
create table sales (
ID numeric,
type varchar(20),
price decimal
);
insert into sales values (1,'bike','900.00');
insert into sales values (2,'bike','100.00');
Query:
select s1.ID, s1.type, s1.price, (s1.price/s2.sum_price) as proportion
from sales s1
inner join (
select type, sum(price) as sum_price
from sales
group by type
) s2
on s1.type = s2.type;
The inner query gets all the sums by type. This is an emulation of the sum(col2) over (partition by col2) which is available in some databases but not in MySQL.