SQL select all by date - sql

Im new with SQL.
I have 3 tables like below:
Table client: a list of clients with their first and last names, full address
Table Produit: a list of articles sent on the web site containing the name of the article, the country where it was manufactured, and its price
Table Actions: a list of all transactions done by clients with the time of the transactions.
Output desired
get the revenue generated by all clients where date is 2018 in each country, and ordered by the highest revenue first.

This should work if you want to include the country, see highest revenue first, & use ANSI function for year.
SELECT c.client_id,
c.first_name,
c.last_name,
p.country,
SUM(a.amount) amount
FROM client c
JOIN actions a ON c.client_id = a.client_id
JOIN produit p ON a.article_id = p.article_id
WHERE EXTRACT(YEAR FROM a.transaction_date) = '2018'
GROUP BY c.client_id,
c.first_name,
c.last_name,
p.country
ORDER BY SUM(a.amount) DESC;

Could do something like this (which assumes that the transactions table has the name of the article, and the produit table has a date field with a date data type):
select
a.client,
sum(c.price) as revenue
from client as a
inner join actions as b
on a.client = b.client
inner join produit c
on c.article = b.article
where year(c.date) = '2018'
group by
a.client
order by
sum(c.price);

Related

SQL how to retrieve last ordered 2 of the listed products from all customers?

This is my current query:
SELECT
c.name, c.email, c.phone, sol.description, so.orderDate
FROM
SalesOrderLine sol
JOIN
SalesOrder so ON sol.salesOrderID = so.id
JOIN
Customer c ON so.customerID = c.id
WHERE
(orderDate >= '2020-05-01' AND orderDate <= '2020-09-09')
AND (description LIKE '%Seed Mix%' OR description LIKE '%Sesame Seeds (Natural)%')
ORDER BY
c.name
Goal
I am aiming to retrieve where product is seed mix or sesame seeds. And between two dates. But only show the most recent date ordered for each customer for both of the products.
Output:
Question
How can I get earliest date they have ordered for both the 1st or 2nd product mentioned in the query?
Desired output:
You can use row_number():
SELECT *
FROM (
SELECT c.name, c.email, c.phone, sol.description, so.orderDate,
RANK() OVER(PARTITION BY c.id, sol.product_id ORDER BY so.orderDate DESC) rn
FROM SalesOrderLine sol
JOIN SalesOrder so ON sol.salesOrderID = so.id
JOIN Customer c ON so.customerID = c.id
WHERE
orderDate >= '20200501'
AND orderDate <= '20200909'
AND (description LIKE '%Seed Mix%' OR description LIKE '%Sesame Seeds (Natural)%')
) t
WHERE rn = 1
ORDER BY name
Note: it seems like you have exact matches on the description. If so, it is more efficient to use equality checks rather than pattern matches. So:
AND description IN ('Seed Mix', 'Sesame Seeds (Natural)')

how to select duplicated column value in sql

Write a query that determines the customer that has spent the most on music for each country. Write a query that returns the country along with the top customer and how much they spent. For countries where the top amount spent is shared, provide all customers who spent this amount.
You should only need to use the Customer and Invoice tables.
i want to select the customer with the maximum money spent for each country and there is two customers have the same money spent and the same country so when using group by country i got only 1 customer what should i do ?
select c.CustomerId,c.FirstName,c.LastName, c.Country , max(c.Invoices) as TotalSpent
from
(select * , sum(i.Total) as 'Invoices'
from Customer d
join Invoice i on i.CustomerId = d.CustomerId
group by i.CustomerId
) c
group by c.Country
the table i got is the same expected table except 1 customer
Consider joining unit level with two aggregate queries: 1) first to calculate total amount by CustomerId and Country and 2) second to calculate max total amount by Country.
Below assumes your database supports Common Table Expression (CTE) using the WITH clause (nearly supported by all major commercial or open-source RDBMS's). CTE here avoids the need to repeat sum_agg as a subquery.
with sum_agg AS (
select i.CustomerId, sub_c.Country, sum(i.Total) as Sum_Amount
from Customer sub_c
join Invoice i on i.CustomerId = sub_c.CustomerId
group by i.CustomerId, sub_c.Country
)
select c.CustomerId, c.FirstName, c.LastName, c.Country, max_agg.Max_Sum
from Customer c
join sum_agg
on c.CustomerId = sum_agg.Customer_Id and c.Country = sum_agg.Country
join
(select Country, max(Sum_Amount) as Max_Sum
from sum_agg
group by Country
) max_agg
on max_agg.Country = sum_agg.Country and max_agg.Max_Sum = sum_agg.Sum_Amount
Your inner query is almost correct. It should be
select d.*, sum(i.Total) as Invoices
from Customer d
join Invoice i on i.CustomerId = d.CustomerId
group by d.CustomerId
It is allowed to use d.* here, as we can assume d.CustomerId to be the table's primary key, so all columns in the table are functionally dependent on it. If we grouped by d.country instead for instance, that would not be the case and d.* would be forbidden in the select clause (as well as d.firstname etc.). We can only select columns we grouped by (directly or indirectly) and aggregates such as MIN, MAX, SUM etc.
This query gives you the totals per customer along with the customers' countries.
But then you are taking this result and group by country. If you do this, you can only access country and its aggregates. Selecting c.CustomerId for instance is invalid, as there is no the customer ID per country. If your DBMS allows this, it it flawed in this regard and you get a kind of random result.
If your DBMS features window functions, you can get the maximum amounts per country on-the-fly:
select customerid, firstname, lastname, country, invoices
from
(
select
c.*,
sum(i.total) as invoices,
max(sum(i.total)) over (partition by c.country) as max_sum
from customer c
join invoice i on i.customerid = c.customerid
group by c.customerid
) per_customer
where invoices = max_sum
order by country, customerid;
Otherwise you'd have to use your inner query twice, once to get the country totals, once to get the customers matching these totals:
select
c.customerid, c.firstname, c.lastname, c.country,
sum(i.total) as invoices
from customer c
join invoice i on i.customerid = c.customerid
group by c.customerid
having (c.country, invoices) in
(
select country, max(invoices)
from
(
select
--c.customerid, <- optional, it may make this query more readable
c.country,
sum(i.total) as invoices
from customer c
join invoice i on i.customerid = c.customerid
group by c.customerid
) per_customer
);

How to query MAX(SUM(relation)) in Postgresql?

I have read several related threads on StackOverflow but none of them solves my problem.
I have a Sales database as where I need to query for the customer who spent the most amount in buying stuff.
For that, I need to find who bought which product using
SELECT sum(qty*rate)
AS exp from salesdetails as s JOIN sales as ss on (ss.invno = s.invno)
JOIN customer as c ON (ss.customerno = c.custno) GROUP BY(c.name)
ORDER BY sum(qty*rate);
It returns a table with the name of the person and what he spent in ascending order as
Output of command above:
While what I actually need is to only print a tuple when sum(qty*rate) is maximum. Currently I'm getting the results by sub querying like:
SELECT name, sum(qty*rate) FROM salesdetails as s JOIN sales as ss on (ss.invno=s.invno)
JOIN customer as c ON (ss.customerno = c.custno) GROUP BY(c.name)
HAVING sum(qty*rate) IN (SELECT max(exp) FROM (SELECT sum(qty*rate)
AS exp from salesdetails as s JOIN sales as ss on (ss.invno = s.invno)
JOIN customer as c ON (ss.customerno = c.custno) GROUP BY(c.name) ORDER BY sum(qty*rate)) aa);
Expected Output:
Is there any shorter way to get to the output?
Are you looking for something like this:
select *
from (
SELECT c.Name, sum(qty*rate)
AS exp from salesdetails as s JOIN sales as ss on (ss.invno = s.invno)
JOIN customer as c ON (ss.customerno = c.custno)
GROUP BY(c.name)
ORDER BY sum(qty*rate) desc
) t
limit 1;
You want row_number() or distinct on:
SELECT DISTINCT ON (c.name) c.name, sum(qty*rate) AS exp
FROM salesdetails s JOIN
sales ss
on (ss.invno = s.invno) JOIN
customer c
ON (ss.customerno = c.custno)
GROUP BY c.name
ORDER BY c.name, sum(qty*rate) DESC;

Filtering a table based on the average of a full column

I have tried to filter a table based on the average of a column, but somehow it doesn't work. It doesn't return anything. The average is about 102 and replacing the AVG clause with it does return the correct rows.
How can I get this to work properly?
SELECT
ce.customer_id,
ce.first_name,
ce.total
FROM
(SELECT
p.customer_id,
c.first_name,
SUM(p.amount) AS total
FROM payment as p
JOIN customer as c ON p.customer_id=c.customer_id
GROUP BY p.customer_id,c.first_name) AS ce
GROUP BY ce.customer_id,ce.first_name,ce.total
HAVING ce.total > AVG(ce.total)
Your query has more than one error. The outer GROUP BY does not make sense.
My recommendation is to use window functions. In this case, you want the average of the total, so this looks like:
SELECT ce.customer_id, ce.first_name, ce.total
FROM (SELECT p.customer_id, c.first_name, SUM(p.amount) AS total,
AVG(SUM(p.amount)) OVER () as avg_total
FROM payment p JOIN
customer c
ON p.customer_id = c.customer_id
GROUP BY p.customer_id, c.first_name
) ce
WHERE ce.total > avg_total

SQL Find next Date for each Customer, SQL For each?

I have some problems with an SQL statement. I need to find the next DeliveryDate for each Customer in the following setup.
Tables
Customer (id)
DeliveryOrder (id, deliveryDate)
DeliveryOrderCustomer (customerId, deliveryOrderId)
Each Customer may have several DeliveryOrders on the same deliveryDate. I just can't figure out how to only get one deliveryDate for each customer. The date should be the next upcoming DeliveryDate after today. I feel like I would need some sort of "for each" here but I don't know how to solve it in SQL.
Another simpler version
select c.id, min(o.date)
from customer c
inner join deliveryordercustomer co o on co.customerId = c.id
inner join deliveryorder o on co.deliveryOrderId = o.id and o.date>getdate()
group by c.id
This would give the expected results using a subselect. Take into account that current_date may be rdbms specific, it works for Oracle.
select c.id, o.date
from customer c
inner join deliveryordercustomer co o on co.customerId = c.id
inner join deliveryorder o on co.deliveryOrderId = o.id
where o.date =
(select min(o2.date)
from deliveryorder o2
where o2.id = co.deliveryOrderId and o2.date > current_date)
You need to use a group by. There's a lot of ways to do this, here's my solution that takes into account multiple orders on same day for customer, and allows you to query different delivery slots, first, second etc. This assumes Sql Server 2005 and above.
;with CustomerDeliveries as
(
Select c.id, do.deliveryDate, Rank()
over (Partition BY c.id order by do.deliveryDate) as DeliverySlot
From Customer c
inner join DeliveryOrderCustomer doc on c.id = doc.customerId
inner join DeliveryOrder do on do.id = doc.deliveryOrderId
Where do.deliveryDate>GETDATE()
Group By c.id, do.deliveryDate
)
Select id, deliveryDate
From CustomerDeliveries
Where DeliverySlot = 1