How to compose select request using many-to-many relationship in PostgreSQL? - sql

I have this tables:
CREATE TABLE orders (
order_id serial PRIMARY KEY
, number_of_things int
);
CREATE TABLE things (
thing_id serial PRIMARY KEY
, cost int
);
CREATE TABLE orders_to_things (
order_id int REFERENCES orders (order_id)
, thing_id int REFERENCES things (thing_id)
);
How to compose a request for select all orders where cost of things more than some number?
I tried to use:
SELECT orders.order_id
FROM orders
INNER JOIN orders_to_things ON (orders_to_things.order_id = orders.order_id)
JOIN things ON (orders_to_things.thing_id=things.thing_id)
WHERE (select SUM(things.cost) FROM things) > *some number*
but didn't get the correct result.

Try this:
SELECT O.order_id, sum(T.cost)
FROM orders O
INNER JOIN orders_to_things ON orders_to_things.order_id = orders.order_id
JOIN things T ON orders_to_things.thing_id=things.thing_id
GROUP BY O.order_id
HAVING T.cost > 'number....'

If you want all order ids, you don't need the orders table. The simplest way to write the query is:
SELECT ott.order_id, sum(t.cost)
FROM orders_to_things ott JOIN
things t
ON ott.thing_id = t.thing_id
GROUP BY ott.order_id
HAVING sum(t.cost) > <number>;

Related

sqlite - join tables or subquery?

I have the following tables:
create table part_category(id text primary key);
create table parts (id text primary key not null,
cat references part_category(id));
create table products (id text primary key not null);
create table product_parts (product references products(id),
part references parts(id),
qty integer);
create table locations (id text primary key not null,
stage text not null);
create table stock (part references parts(id),
cat references part_category(id),
location references locations(id),
qty integer,
date text);
create table orders (part references parts(id),
cat references parts(cat),
product references products(id),
qty integer not null default 0,
date_order text,
date_due text,
date_done text,
status boolean,
primary key(part, product, date_due));
And I'd like to have this returned from a select:
Part, Category, Product, Qty, Date Ordered, Date Due, qty of material, qty of stock, qty of wip
The columns bolded above are the ones that I can't figure out. Below is my select with the subquery where I'm trying to get the qty of stock.
The problem is the query is returning zero for everything.
orders = db.execute('''select distinct o.part, o.cat, o.product, o.qty,
o.date_order, o.date_due, o.date_done,
julianday(date_due) - julianday(date_order) as days_due,
(select stock.qty from stock, orders
where stock.part = orders.part and stock.location = 'stock' and orders.status = 1)
as qty_stock
from orders as o join stock as s on o.part = s.part
where o.status = 1
order by o.date_due asc, o.product asc, o.part asc''').fetchall()
Example output is
for item in orders:
print item['part'], item['qty'], item['qty_stock']
SOME_PART_NUMBER 3 0
But should be:
SOME_PART_NUMBER 3 22
I'm unsure about your business logic.
I guess this is what you want.
select distinct o.part, o.cat, o.product, o.qty,
o.date_order, o.date_due, o.date_done,
julianday(date_due) - julianday(date_order) as days_due,
qs.stockQuantity as qty_stock
from orders as o
join stock as s on o.part = s.part
left join (select stock.part, sum(stock.qty) stockQuantity
from stock ss
join orders oo on ss.part = oo.part
where ss.location = 'stock' and oo.status = 1
group by stock.part
) qs on qs.part = o.part
where o.status = 1
order by o.date_due asc, o.product asc, o.part asc
The title says "join tables OR subquery". The sql does both. I'm not sayin' that's the problem. But it certainly adds a level of complexity that could be error prone. You could try removing the subquery and replace it with s.qty, then add s.location = "stock" to the WHERE clause.

Calculating average price of items purchased by customers

I have three tables: customer, order and line items. They are set up as follows:
CREATE TABLE cust_account(
cust_id DECIMAL(10) NOT NULL,
first VARCHAR(30),
last VARCHAR(30),
address VARCHAR(50),
PRIMARY KEY (cust_id));
CREATE TABLE orders(
order_num DECIMAL(10) NOT NULL,
cust_id DECIMAL(10) NOT NULL,
order_date DATE,
PRIMARY KEY (order_num));
CREATE TABLE lines(
order_num DECIMAL(10) NOT NULL,
line_id DECIMAL(10) NOT NULL,
item_num DECIMAL(10) NOT NULL,
price DECIMAL(10),
PRIMARY KEY (order_id, line_id),
FOREIGN KEY (item_id) REFERENCES products);
Using Oracle, I need to write a query that presents the average item price for for those customers that made more than 5 or more purchases. This is what I've been working with:
SELECT DISTINCT cust_account.cust_id,cust_account.first, cust_account.last, lines.AVG(price) AS average_price
FROM cust_account
JOIN orders
ON cust_account.cust_id = orders.cust_id
JOIN lines
ON lines.order_num = orders.order_num
WHERE lines.item_num IN (SELECT lines.item_num
FROM lines
JOIN orders
ON lines.order_num = orders.order_num
GROUP BY lines.order_num
HAVING COUNT(DISTINCT orders.cust_id) >= 5
);
... INNER JOIN all your tables together
... GROUP BY customer and compute the average price of each customer's lines
... use a HAVING clause to limit the results to groups having 5 or more purchases
Query:
SELECT ca.first, ca.last, avg(l.price) avg_price
FROM cust_account ca
INNER JOIN orders o ON o.cust_id = ca.cust_id
INNER JOIN lines l ON l.order_num = o.order_number
GROUP BY ca.first, ca.last
HAVING COUNT(distinct l.line_id) >=5
-- OR, maybe your requirement is ...
-- HAVING COUNT(distinct o.order_num) >= 5
-- ... the question was a bit unclear on this point
I think this is it. I don't think it will work right away (I know nothing about oracle) but I think you will get the idea:
SELECT orders.cust_id,
AVG(lines.price) AS average_price
FROM lines
JOIN orders ON orders.order_num = orders.order_num
WHERE orders.cust_id IN (SELECT orders.cust_id
FROM orders
GROUP BY orders.cust_id
HAVING COUNT(*) >= 5)
GROUP BY orders.cust_id;
Subquery selects customers that have more than 5 orders.
And main query just gets all lines from all orders made by this customers.
I guess you can eliminate subquery by using HAVING DISTINCT .... Anyways, one with subquery should work just fine.
UPD.
something like this
SELECT orders.cust_id,
AVG(lines.price) AS average_price
JOIN orders ON orders.order_num = orders.order_num
GROUP BY orders.cust_id
HAVING COUNT(DISTINCT orders.id) >= 5;

Writing a query to combine results from multiple tables with all possible combinations

I have this database schema:
CREATE TABLE users (
id SERIAL PRIMARY KEY,
name char(50) NOT NULL UNIQUE
);
CREATE TABLE products (
id SERIAL PRIMARY KEY,
name char(50) NOT NULL,
);
CREATE TABLE orders (
id SERIAL PRIMARY KEY,
uid INTEGER REFERENCES users (id) NOT NULL,
pid INTEGER REFERENCES products (id) NOT NULL,
quantity INTEGER NOT NULL,
price FLOAT NOT NULL CHECK (price >= 0)
);
I am trying to write a query that will give me all combinations of users and products, as well as the total amount spent by the user on that product. Specifically, if I have 5 products and 5 users, there should be 25 rows in the table. Right now I have a query that almost gets the job done, however, if the user has never purchased that product then there is no row printed at all.
Here's what I've written so far:
SELECT u.name as username, p.name as productname, SUM(o.quantity * o.price) as totalPrice
FROM users u, orders o, products p
WHERE u.id = o.uid
AND p.id = o.pid
GROUP BY u.name, p.name
ORDER BY u.name, p.name
I figure that this requires some sort of join, but my SQL knowledge is limited and I am not sure what would be the best way to go about doing this. I think if somebody can help me figure this out then I will have a much better understanding.
You can do this using cross join and left join:
select u.name as username, p.name as productname,
sum(o.quantity * o.price) as totalPrice
from users u cross join
products p left join
orders o
on o.uid = u.id and o.pid = p.id
group by u.name, p.name;
The cross join generates all the rows. The left join brings in the matching rows. A simple rule when using SQL is: Never use commas in the FROM clause. Always use explicit JOIN syntax.

SQL multiple natural inner joins

Why does this correctly return the Order ID of an order, the Customer ID of the person who made the order, and the Last Name of the employee in charge of the transaction
SELECT "OrderID", "CustomerID", "LastName"
FROM orders O
NATURAL INNER JOIN customers JOIN employees ON O."EmployeeID" = employees."EmployeeID";
while
SELECT "OrderID", "CustomerID", "LastName"
FROM orders O
NATURAL INNER JOIN customers NATURAL INNER JOIN employees;
returns 0 rows?
I am sure that they have common columns.
Table orders
OrderId
EmployeeID
CustomerID
...
Table employees
EmployeeID
...
Table customers
CustomerID
...
Without seeing your full, unedited schema it's hard to be sure, but I'd say there are more common columns than you intended.
E.g. as #ClockworkMuse sugested:
CREATE TABLE orders (
OrderId integer primary key,
EmployeeID integer not null,
CustomerID integer not null,
created_at timestamp not null default current_timestamp,
...
);
CREATE TABLE employees (
EmployeeID integer primary key,
created_at timestamp not null default current_timestamp,
...
);
then orders NATURAL JOIN employees will be equivalent to orders INNER JOIN employees USING (EmployeeID, created_at). Which surely isn't what you intended.
You should use INNER JOIN ... USING (colname) or INNER JOIN ... ON (condition).
NATURAL JOIN is a poorly thought out feature that should really be avoided except on quick and dirty ad-hoc queries, if even then. Even if it works now, if you later add an unrelated column to a table it might change the meaning of existing queries. That's ... well, avoid natural joins.

Removing Duplicate values within a Query when Distinct doesn't work? SQL

Here is the current query I am running.
select c.customer_name, c.city, c.credit_limit, sum(ol.quoted_price)
from customer c, order_line ol, (select order_num from order_line where part_num = 'AT94') t1
where ol.order_num = t1.order_num and customer_num in ( select customer_num
from orders
where order_num in (select t1.order_num
from order_line,(select order_num from order_line where part_num = 'AT94') t1
INNER JOIN
(select order_num from order_line where part_num = 'BV06') t2
on t1.order_num = t2.order_num
where t1.order_num = order_line.order_num
group by t1.order_num))
group by t1.order_num, c.customer_name, c.city, c.credit_limit
The current output I am receiving is:
I wish to obviously remove the duplication located in the output and currently have no idea how to do so. I have tried using unique in the multiple sub-queries with no success.
Any help is great! Thanks.
Here is the database creation.
CREATE TABLE CUSTOMER
(
CUSTOMER_NUM CHAR(3) PRIMARY KEY,
CUSTOMER_NAME CHAR(35) NOT NULL,
STREET CHAR(15),
CITY CHAR(15) DEFAULT 'Ottawa',
PROVINCE CHAR(3),
ZIP CHAR(5),
BALANCE DECIMAL(8,2),
CREDIT_LIMIT DECIMAL(8,2),
REP_NUM CHAR(2)
CONSTRAINT CHK_Limit CHECK (CREDIT_LIMIT >= BALANCE)
);
CREATE TABLE ORDERS
(
ORDER_NUM CHAR(5) PRIMARY KEY,
ORDER_DATE DATE,
CUSTOMER_NUM CHAR(3)
);
CREATE TABLE PART
(
PART_NUM CHAR(4) PRIMARY KEY,
DESCRIPTION CHAR(15),
ON_HAND DECIMAL(4,0),
CLASS CHAR(2),
WAREHOUSE CHAR(1),
PRICE DECIMAL(6,2)
);
CREATE TABLE ORDER_LINE
(
ORDER_NUM CHAR(5),
PART_NUM CHAR(4),
NUM_ORDERED DECIMAL(3,0),
QUOTED_PRICE DECIMAL(6,2),
PRIMARY KEY (ORDER_NUM, PART_NUM)
);
The solution I would like where the order does not matter for the column:
Al's.. | Barrhaven | 7500.00 | 21.95
John.. | Toronto | 10000.00 | 311.95
All data used. Quite a large sum of text. Just decided Pastebin instead of making this question that much longer.
https://pastebin.com/ASBzqcJq
You basically need to see which customer has bought both of parts_numb AT94 and BV06. The issue you're facing is that your query returning duplicated rows, this is because your query already have some redundancy. Which made me go back and check the giving results manually to get the correct results out of the sample that you've provided.
SELECT
ol.ORDER_NUM,
c.customer_name,
c.city,
c.credit_limit,
sum(ol.quoted_price)
FROM #order_line ol
INNER JOIN #Orders o ON o.order_num = ol.order_num
JOIN (SELECT ORDER_NUM FROM #ORDER_LINE WHERE part_num = 'AT94') t1 ON t1.ORDER_NUM = o.ORDER_NUM
JOIN (SELECT ORDER_NUM FROM #ORDER_LINE WHERE part_num = 'BV06') t2 ON t2.ORDER_NUM = o.ORDER_NUM
INNER JOIN #customer c ON c.customer_num = o.customer_num
WHERE
ol.ORDER_NUM IN(t1.ORDER_NUM)
AND ol.ORDER_NUM IN(t2.ORDER_NUM)
GROUP BY
ol.ORDER_NUM,
c.customer_name,
c.city,
c.credit_limit
SQLFiddle
Hope this will save your day
I can see that your result set already has distinct records. Your result set does not have any two rows with exactly same values. What is the output you are expecting? I am not sure what your requirement is. But I think the join between customer number in Customer table and Customer number in Orders table is missing.
Are you trying to do something like this?
select C.customer_name, C.city, C.credit_limit, sum(OL.quoted_price)
from CUSTOMER C
join ORDERS O ON C.Customer_num=O.Customer_num
join ORDER_LINE OL on O.Order_num=Ol.Order_num
WHERE part_num in('AT94','BV06')
group by C.customer_name, C.city, C.credit_limit;