Get the results of a subquery in SQL - sql

How do you create a join to get the latest invoice for all customers?
Tables:
- Invoices
- Customers
Customers table has: id, last_invoice_sent_at, last_invoice_guid
Invoices table has: id, customer_id, sent_at, guid
I'd like to fetch the latest invoice for every customer and, with that data, update last_invoice_sent_at and last_invoice_guid in the Customers table.

You want to use distinct on. For a query soring by customer_id and then by invoice, it would return the first row for each distinct value indicated in distinct on. That is the rows with * below:
customer_id | sent_at |
1 | 2014-07-12 | *
1 | 2014-07-10 |
1 | 2014-07-09 |
2 | 2014-07-11 | *
2 | 2014-07-10 |
So your update query could look like:
update customers
set last_invoice_sent_at = sent_at
from (
select distinct on (customer_id)
customer_id,
sent_at
from invoices
order by customer_id, sent_at desc
) sub
where sub.customer_id = customers.customer_id

#Konrad provided a flawless SQL statement. But since we are only interested in a single column, GROUP BY will be more efficient than DISTINCT ON (which is great to retrieve multiple columns from the same row):
UPDATE customers c
SET last_invoice_sent_at = sub.last_sent
FROM (
SELECT customer_id, max(sent_at) AS last_sent
FROM invoices
GROUP BY 1
) sub
WHERE sub.customer_id = c.customer_id;

Related

SQL Query find users with only one product type

I solemnly swear I did my best to find an existing question, may I'm not sure how to phrase it correctly.
I would like to return records for users that have quota for only one product type.
| user_id | product |
| 1 | A |
| 1 | B |
| 1 | C |
| 2 | B |
| 3 | B |
| 3 | C |
| 3 | D |
In the example above I'd like a query that only returns users who carry quota for only one product type - doesn't really matter which product at this point.
I tried using select user_id, product from table group by 1,2 having count(user) < 2 but this does not work, nor does select user_id, product from table group by 1,2 having count(*) < 2
Any help is appreciated.
Your having clause is good; the issue's with your group by. Try this:
select user_id
, count(distinct product) NumberOfProducts
from table
group by user_id
having count(distinct product) = 1
Or you could do this; which is closer to your original:
select user_id
from table
group by user_id
having count(*) < 2
The group by clause can't take ordinal arguments (like, e.g., the order by clause can). When grouping by a value like 1, you're in fact grouping by the literal value 1, which would just be the same for any row in the table, and thus will group all the rows in the table to one group. Since there are more than one product in the entire table, no rows will be returned.
Instead, you should group by the user_id:
SELECT user_id
FROM mytable
GROUP BY user_id
HAVING COUNT(*) = 1
If you want the product, then do:
select user_id, max(product) as product
from table
group by user_id
having min(product) = max(product);
The having clause could also be:
having count(distinct product) = 1

PostgreSQL list companies and rank by sales

So I have:
companies (id, name, tenant_id)
invoices (id, company_id, tenant_id, total)
What I want to do is return a result set like:
company | Feb Sales | Feb Rank | Lifetime Sales | Lifetime Rank
-----------------------------------------------------------------------
ABC Comp | 1,000 | 1 | 2,000 | 2
XYZ Corp | 500 | 2 | 5,000 | 1
I can do the sales totals using subselects, but when I do the rank always returns 1. I'm assuming because it only returns 1 row per subselect so will always be the top row?
Here is a piece of the sql:
SELECT
"public".companies."name",
(
SELECT
rank() OVER (PARTITION BY i.tenant_id ORDER BY sum(grand_total) DESC) AS POSITION
FROM
invoices i
where
company_id = companies.id
group by
i.tenant_id, i.company_id
)
from companies
Below is untested version that can have typos. Please treat it just as description of the approach. For simplicity I assumed that invoices have a month column.
SELECT
"public".companies."name",
rank() OVER (PARTITION BY sales.companies ORDER BY sales.lifetime) As "Lifetime Rank",
rank() OVER (PARTITION BY sales.companies ORDER BY sales.month As "One Month"
FROM companies LEFT JOIN
(
SELECT
SUM(grand_total) As Lifetime,
SUM(CASE WHEN i.month = <the month of report>, grand_total, 0) As Month
FROM
invoices i
GROUP BY company_id
) sales
ON companies.company_id = sales.company_id
If you run into problems, add the actual code that you used and sample data to your post and I will attempt to create a live demo for you.

SQL find which products appear every week

I have some queries that return lists of products for a time period, and I want to find out which products appear in all of those time periods.
WeekEnding | Product
07/07/14 | A
07/07/14 | B
07/07/14 | C
14/07/14 | A
14/07/14 | B
21/07/14 | A
21/07/14 | B
21/07/14 | C
So in the above example data I would have products A, and B which are in all 3 weeks, and I could imagine running a query like
SELECT Product FROM ProductWeek
GROUP BY Product
HAVING COUNT(*) = (
SELECT COUNT(distinct weekending) from ProductWeek )
Unfortunately I am writing this query in MSAccess, so count distinct is not available, but at any rate it feels like there should be a more elegant solution to this problem
You can do it with a subquery:
SELECT Product
FROM ProductWeek
GROUP BY Product
HAVING COUNT(*) = (SELECT COUNT(*) from (SELECT distinct weekending from ProductWeek ) as t);

SQL Query - Sum of Order tha Contain an Item

I am surprised I have not been able to find a solution to this. We have a table
ORDER # | PRODUCT ID | PRICE
1 | 1 | 1.00
1 | 2 | 2.00
2 | 3 | 3.00
2 | 4 | 4.00
3 | 1 | 5.00
3 | 4 | 6.00
We want to capture the sum of the revenues of all orders which included productID=1. The result in this example should be 1+2+5+6 = 14
What is the best way to achieve this?
Currently, best solution I have is to run two queries.
1 - SELECT orderID FROM table WHERE prodID=$prodID
2 - SELECT price FROM table WHERE orderID=[result of the above]
This has worked, but would strongly prefer to have a single query.
Here is a query that gives the results you are looking for:
SELECT OrderNum, SUM(PRICE) as TotalPrice
FROM MyTable AS M
WHERE EXISTS (SELECT 1 -- Include only orders that contain product 1
FROM MyTable AS M2
WHERE M2.OrderNum=M.OrderNum AND M2.ProductId=1)
GROUP BY OrderNum
Try:
select sum(price) as total_price
from orders
where prod_order in
(select prod_order
from orders
where product_id = 1)
Check this SQLFiddle to confirm the result.
select sum(price) as total_price where product_id=[enter here id];
SELECT SUM(t1.price) FROM tableName t1 WHERE
t1.orderId IN (SELECT t2.orderId FROM tableName t2 WHERE
t2.productId=productIdYouWant)
If you need more informations on how this work, please feel free to ask.
You need a nested select. The inner select should give you the total order value;
select order, sum(price) as totalvalue from table group by order
Now you need to select the orders which have a product id of 1, and sum the order price;
select sum(totalvalue) from (
select order, sum(price) as totalvalue from table group by order
) where order in (
select order from table where productid = 1
)

SQL Server - Most recent date and sale amount columns

I'm using SQL Server 2008 R2 to complete a query. I have a sale table that contains a unique sale id, a customer id, a sale date, and a sale amount. I'm trying to create a table that has the most recent sale for each customer and the amount for that sale.
| customer_id | most recent sale date | sale amount |
| 1 |2012-06-11 00:00:00.000| 150 |
| 2 |2012-01-07 00:00:00.000| 55 |
| 3 |2012-02-18 00:00:00.000| 117 |
| 4 |2012-09-02 00:00:00.000| 25 |
I have the first two columns with this query:
SELECT DISTINCT customer_id, MAX(sale_date)
FROM sale
GROUP BY customer_id
When I try to add the amount of the sale, everything I try includes every sale for that customer, not just the most recent one. Is there a way to do this? Keep in mind there is a unique sale id on this table that might be of some use. Thank you for your time.
You can use ROW_NUMBER with PARTITION BY in a CTE:
WITH CTE AS
(
SELECT sale_id,customer_id,sale_date, sale_amount
, RN = ROW_NUMBER() OVER (PARTITION BY customer_id ORDER BY sale_date DESC)
FROM sale
)
SELECT sale_id, customer_id, sale_date, sale_amount
FROM CTE
WHERE RN = 1
Here's a sample fiddle: http://sqlfiddle.com/#!3/513280/1/0
SELECT a.customer_id, a.sale_date, a.sale_amount
FROM sale a
INNER JOIN
(
SELECT customer_id, MAX(sale_date) maxSale
FROM sale
GROUP BY customer_id
) b ON a.customer_ID = b.customer_ID AND
a.sale_date = b.maxSale
By just using Aggregate function MAX in an independent subquery worked for me.
SELECT Customerid, Saleid, Sldate AS 'Most Recent Sale', Saleamount
FROM Sale S1
WHERE Sldate = (SELECT MAX(Sldate) FROM Sale S2);
Using Aliases is a mandatory step.
Hope this helps!
SELECT CUSTOMER_ID,SALE_AMOUNT,SALEDATE FROM SALE WHERE SALEDATE =(SELECT MAX(SALEDATE) FROM SALE);