qrying DB where two customers in one order - sql

lets say I have two tables,
Order
order_id (PK)
ordered_date
CustomerOrders
Customer_order_id (PK)
order_id (FK)
customer_type(char1) ( can be S, T and M)
If one or more different types of customers involved in an order, the table will look like
Order
order_id 5
order_date '05-06-2020'
CusotmerOrder
customer_order_id 1
order_id 5
type 'M'
customer_order_id 2
order_id 5
type 'S'
and so on
How can I write a qry that will return all unique order_ids that have combination of S and M type customers?

It is easy self join query:
SELECT DISTINCT M.order_id
FROM CustomerOrders AS M
INNER JOIN CustomerOrders AS S
ON M.order_id = S.order_id
WHERE M.customer_type = 'M'
AND S.customer_type = 'S'

You can use exists:
Select distinct order_id
from CustomerOrder co
where exists (select * from CustomerOrder co1
where co.order_id = co1.order_id and co1.Type = 'M') and
exists (select * from CustomerOrder co1
where co.order_id = co1.order_id and co1.Type = 'S');

Related

Join data from three related tables

In Postgres I have the following 4 tables
user (
id,
name
);
order(
id,
user_id
)
service(
id,
name
);
order_to_service(
id,
order_id,
service_id
price
);
I want to write a query to see User name , amount of orders of this user and how much money he spend on all orders
Example:
name amount price
Albert 100 3200
This is my query
select u.name , count(o.user_id),sum(ots.price)
from orders o inner join users u on u.id=o.user_id
inner join order_to_service ots on ots.order_id = o.id
where(o.user_id is not null) group by u.name
This is my result:
"Аlbert";55;29978
Accodring to this result user with name Albert has 55 orders
But using this query
select count(*) from orders where user_id = (select id from users where name like 'Albert')
Result is 33
What's wrong with my first query?
If the relationship between Orders table and Order_to_service table is one to many, then you would need to sum up the price per order in Order_to_service table before joining with the Orders table. Try this:
select u.name , count(o.user_id),sum(ots.price_tot)
from orders o inner join users u on u.id=o.user_id
inner join ( select order_id, sum(price) as price_tot
from order_to_service
group by order_id ) ots
on ots.order_id = o.id
where (o.user_id is not null) group by u.name

Confused with sql query

I have the following tables:
Customer(login varchar, town varchar)
Orders (Ordno int , login varchar) >>login Fk from Customer
combination (Id int, ordno int ,Product_Id int) >>ordno fk from orders
I need to show the products that have been sold in ALL the cities.
Example:
Insert into Customer (log1 , NY) (log2, NY) (log3, London)
Insert into Orders (1,log1) (2,log1) (3,log3)
Insert into combination (1,1,1) (2,2,2) (3,3,1)
Product 1 sold in NY
product 2 sold in NY
product 1 sold in London
if the available cities are only NY and London, then the only product that must be the result of the query is product 1
SELECT a.ProductID
FROM Combination a
INNER JOIN Orders b
ON a.OrdNo = b.OrdNo
INNER JOIN Customer c
ON b.Login = c.LogIn
GROUP BY a.ProductID
HAVING COUNT(DISTINCT a.ID) = (SELECT COUNT(DISTINCT town) FROM Customer)
SQLFiddle Demo
Not sure what exactly you are trying to do here.
SELECT c.Town, cc.Product_Id FROM from Customer c
JOIN Orders o ON c.login = o.login
JOIN Combination cc ON o.Ordno = cc.ordNo
GROUP BY c.town
This will group the towns together and show you the Product_Id
You still need a Product table to display the product table.
This query excludes the product table
Assuming Products table looks like this:
Products (Product_Id int, Name)
You need to join stuff all the way down (or up) to customer...
SELECT p.Name, c.town
FROM Products p
INNER JOIN Combination comb ON comb.Product_Id=p.Product_Id
INNER JOIN Orders o ON o.Ordno=comb.ordno
INNER JOIN Customer cust ON cust.login=o.login
GROUP BY p.Name, c.town

Last order item in Oracle SQL

I need to list columns from customer table, the date from first order and all data from last one, in a 1:N relationship between customer and order tables. I'm using Oracle 10g.
How the best way to do that?
TABLE CUSTOMER
---------------
id NUMBER
name VARCHAR2(200)
subscribe_date DATE
TABLE ORDER
---------------
id NUMBER
id_order NUMBER
purchase_date DATE
purchase_value NUMBER
Here is one way of doing it, using the row_number function, one join, and on aggregation:
select c.*,
min(o.purchase_date) as FirstPurchaseDate,
min(case when seqnum = 1 then o.id_order end) as Last_IdOrder,
min(case when seqnum = 1 then o.purchase_date end) as Last_PurchaseDate,
min(case when seqnum = 1 then o.purchase_value end) as Last_PurchaseValue
from Customer c join
(select o.*,
row_number() over (partition by o.id order by purchase_date desc) as seqnum
from orders o
) o
on c.customer_id = o.order_id
group by c.customer_id, c.name, c.subscribe_date
It's not obvious how to join the customer table to the orders table (order is a reserved word in Oracle so your table can't be named order). If we assume that the id_order in orders joins to the id in customer
SELECT c.id customer_id,
c.name name,
c.subscribe_date,
o.first_purchase_date,
o.id last_order_id,
o.purchase_date last_order_purchase_date,
o.purchase_value last_order_purchase_value
FROM customer c
JOIN (SELECT o.*,
min(o.purchase_date) over (partition by id_order) first_purchase_date,
rank() over (partition by id_order order by purchase_date desc) rnk
FROM orders o) o ON (c.id = o.id_order)
WHERE rnk = 1
I'm confused by your field names, but I'm going to assume that ORDER.id is the id in the CUSTOMER table.
The earliest order date is easy.
select CUSTOMER.*, min(ORDER.purchase_date)
from CUSTOMER
inner join ORDER on CUSTOMER.id = ORDER.id
group by CUSTOMER.*
To get the last order data, join this to the ORDER table again.
select CUSTOMER.*, min(ORD_FIRST.purchase_date), ORD_LAST.*
from CUSTOMER
inner join ORDER ORD_FIRST on CUSTOMER.id = ORD_FIRST.id
inner join ORDER ORD_LAST on CUSTOMER.id = ORD_LAST.id
group by CUSTOMER.*, ORD_LAST.*
having ORD_LAST.purchase_date = max(ORD_FIRST.purchase_date)
Maybe something like this assuming the ID field in the Order table is actually the Customer ID:
SELECT C.*, O1.*, O2.purchase_Date as FirstPurchaseDate
FROM Customer C
LEFT JOIN
(
SELECT Max(purchase_date) as pdate, id
FROM Orders
GROUP BY id
) MaxPurchaseOrder
ON C.Id = MaxPurchaseOrder.Id
LEFT JOIN Orders O1
ON MaxPurchaseOrder.pdate = O1.purchase_date
AND MaxPurchaseOrder.id = O1.id
LEFT JOIN
(
SELECT Min(purchase_date) as pdate, id
FROM Orders
GROUP BY id
) MinPurchaseOrder
ON C.Id = MinPurchaseOrder.Id
LEFT JOIN Orders O2
ON MinPurchaseOrder.pdate = O2.purchase_date
AND MinPurchaseOrder.id = O2.id
And the sql fiddle.

How to join tables together based on subselects?

I'm stuck to figure out how to write a query. Basically I've three tables (Orders, Products, Orders_Products) which I want to join together and apply some filtering.
Orders table:
ORDER_ID CUSTOMER_ID
1 1
2 2
Products table:
PRODUCT_ID PRODUCT_NAME PRODUCT_TITLE
1 'P1' 'T1'
2 'P1' 'T2'
3 'P2' 'T3'
4 'P2' 'T4'
5 'P2' 'T5'
6 'P3' 'T6'
Orders_Products table:
ORDER_ID PRODUCT_ID
1 1
1 3
2 1
2 3
2 6
For example I want to get all Orders which consists (exactly) of the products P1/T1 and P2/T3. I tried something like this, but that doesn't work:
SELECT * FROM Orders
LEFT JOIN Orders_Products ON Orders_Products.ORDER_ID = Orders.ORDER_ID
LEFT JOIN Products ON Orders_Products.PRODUCT_ID = Products.PRODUCT_ID
WHERE EXISTS (SELECT * FROM Product WHERE PRODUCT_NAME = 'P1' AND PRODUCT_TITLE = 'T1')
AND EXISTS (SELECT * FROM Product WHERE PRODUCT_NAME = 'P2' AND PRODUCT_TITLE = 'T3');
EDIT: To clarify what I really have to achieve. The user should be able to search for orders matching the given products. The user enters one or more product name / product title combinations and gets all the orders which have exactly this products associated. What I get (from a web application) are only the name/title combinations and I have to use those in a query to get the ORDER_ID.
SELECT OrderID, COUNT(*) AS ProductsCount
FROM Orders_Products
WHERE (PRODUCT_ID = 1 OR PRODUCT_ID = 3)
GROUP BY OrderID
HAVING COUNT(*) = 2
EDIT: Please ignore the above statement. See if the following works.
SELECT OrderID,
SUM(CASE PRODUCT_ID WHEN 1 THEN 1 WHEN 3 THEN 1 ELSE 3 END)
AS ProductsCount
FROM Orders_Products
GROUP BY OrderID
HAVING SUM(CASE PRODUCT_ID WHEN 1 THEN 1 WHEN 3 THEN 1 ELSE 3 END) = 2
I guess this should get you Orders which has only these 2 products.
You probably cannot write simple queries in MySQL to achieve this. But ANSI SQL supports table value constructor which simplifies this type of query.
This basic query returns the full list of orders (5 rows):
SELECT * FROM Products
JOIN Orders_Products ON Orders_Products.PRODUCT_ID = Products.PRODUCT_ID
JOIN Orders ON Orders_Products.ORDER_ID = Orders.ORDER_ID
This query with table value constructor returns the orders that you need:
SELECT * FROM Products
JOIN Orders_Products ON Orders_Products.PRODUCT_ID = Products.PRODUCT_ID
JOIN Orders ON Orders_Products.ORDER_ID = Orders.ORDER_ID
LEFT JOIN (VALUES('P1', 'T1'), ('P2', 'T3')) V(P_NAME, P_TITLE) ON PRODUCT_NAME = P_NAME AND PRODUCT_TITLE=P_TITLE
This query groups the above to returns the ORDER_ID where there is no order outside the required list (eliminating the orders that have rows containing null):
SELECT ORDER_ID FROM Products
JOIN Orders_Products ON Orders_Products.PRODUCT_ID = Products.PRODUCT_ID
JOIN Orders ON Orders_Products.ORDER_ID = Orders.ORDER_ID
LEFT JOIN (VALUES('P1', 'T1'), ('P2', 'T3')) V(P_NAME, P_TITLE) ON PRODUCT_NAME = P_NAME AND PRODUCT_TITLE=P_TITLE
GROUP BY ORDER_ID HAVING COUNT(*) = 2
Among open source databases, HSQLDB is one that supports table value constructor and other user friendly features of ANSI SQL:2008

Optimize SQL query for canceled orders

Here is a subset of my tables:
orders:
- order_id
- customer_id
order_products:
- order_id
- order_product_id (unique key)
- canceled
I want to select all orders (order_id) for a given customer(customer_id), where ALL of the products in the order are canceled, not just some of the products. Is there a more elegantly or efficient way of doing it than this:
select order_id from orders
where order_id in (
select order_id from orders
inner join order_products on orders.order_id = order_products.order_id
where order_products.customer_id = 1234 and order_products.canceled = 1
)
and order_id not in (
select order_id from orders
inner join order_products on orders.order_id = order_products.order_id
where order_products.customer_id = 1234 and order_products.canceled = 0
)
If all orders have at least one row in order_products, Try this
Select order_id from orders o
Where Not Exists
(Select * From order_products
Where order_id = o.order_id
And cancelled = 1)
If the above assumption is not true, then you also need:
Select order_id from orders o
Where Exists
(Select * From order_products
Where order_id = o.order_id)
And Not Exists
(Select * From order_products
Where order_id = o.order_id
And cancelled = 1)
The fastest way will be this:
SELECT order_id
FROM orders o
WHERE customer_id = 1234
AND
(
SELECT canceled
FROM order_products op
WHERE op.order_id = o.order_id
ORDER BY
canceled DESC
LIMIT 1
) = 0
The subquery will return 0 if and only if there had been some products and they all had been canceled.
If there were no products at all, the subquery will return NULL; if there is at least one uncanceled product, the subquery will return 1.
Make sure you have an index on order_products (order_id, canceled)
Something like this? This assumes that every order has at least one product, otherwise this query will return also orders without any products.
select order_id
from orders o
where not exists (select 1 from order_products op
where canceled = 0
and op.order_id = o.order_id
)
and o.customer_id = 1234
SELECT customer_id, order_id, count(*) AS product_count, sum(canceled) AS canceled_count
FROM orders JOIN order_products
ON orders.order_id = order_products.order_id
WHERE customer_id = <<VALUE>>
GROUP BY customer_id, order_id
HAVING product_count = canceled_count
You can try something like this
select orders.order_id
from #orders orders inner join
#order_products order_products on orders.order_id = order_products.order_id
where order_products.customer_id = 1234
GROUP BY orders.order_id
HAVING SUM(order_products.canceled) = COUNT(order_products.canceled)
Since we don't know the database platform, here's an ANSI standard approach. Note that this assumes nothing about the schema (i.e. data type of the cancelled field, how the cancelled flag is set (i.e. 'YES',1,etc.)) and uses nothing specific to a given database platform (which would likely be a more efficient approach if you could give us the platform and version you are using):
select op1.order_id
from (
select op.order_id, cast( case when op.cancelled is not null then 1 else 0 end as tinyint) as is_cancelled
from #order_products op
) op1
group by op1.order_id
having count(*) = sum(op1.is_cancelled);