Select Distinct in inner join with full customer record - sql

I have two tables one with customer and another with invoices. I need to find all customer that have an more that one invoice with different days with in a period.
Invoice table:
Accountnum
Datein
IStatus
...
Customer table:
Accountnum
...
I have two problems:
1:
I can get the customers that have more than one invoice, but I don't know how to check if they are different days.
2:
Customer shows more than one time in this query they need to show only ones.
SELECT c.*
FROM Invoice I
INNER JOIN Customer C
ON I.Accountnum= C.Accountnum
WHERE EXISTS(SELECT DISTINCT I.AccountnumFROM Invoice
WHERE C.Accountnum = I.Accountnum
and i.Datein >= '2020-03-01' and i.Datein <= '2020-05-31'
and (IStatus <> 'V' or IStatus IS NULL)
GROUP BY I.Accountnum
HAVING COUNT(*) > 1)

A simple way to check if a given customer has invoices on two different dates is to ensure that the minimum invoice date differs from the maximum invoice date. You could write this as a join query:
select c.*
from customer c
inner join (
select accountnum
from invoice
where
datein >= '2020-03-01' and datein <= '2020-05-31'
and (istatus <> 'V' or istatus is null)
group by accountnum
having min(datein) <> max(datein)
) i on i.accountnum = c.accountnum

You are close, but no JOIN in the outer query:
SELECT c.*
FROM Customer C
WHERE (SELECT COUNT(*)
FROM Invoice
WHERE C.Accountnum = I.Accountnum AND
i.Datein >= '2020-03-01' and i.Datein <= '2020-05-31' AND
(IStatus <> 'V' or IStatus IS NULL)
) > 1;
Note that I also changed the logic of the subquery. The subquery returns a count which is then compared to 1, rather than using EXISTS.

Related

How to select customers that didn't place an order in the last 7 days

I am trying to find out the customers that didn't place an order in the last seven days. Basically I have 3 tables: customers, orders and help_desk_agents.
I'm trying to figure out the best way to get this information.
The SQL bellow retrieves the customers info, the help desk agent 111 and the last date of the orders of each customer:
SELECT DISTINCT customers.customer_id,
customers.customer_name,
agents.help_desk_agent,
Max(orders.order_date)
FROM customers
LEFT JOIN (SELECT DISTINCT customers.customer_id,
orders.order_date
FROM orders
GROUP BY 1,
2) orders2
ON customers.customer_id = orders2.customer_id
LEFT JOIN help_desk_agents
ON customers.help_desk_agent_id =
help_desk_agents.help_desk_agent_id
WHERE customer.help_desk_agent_id = 111
GROUP BY 1,
2,
3
I would like like somehow to filter the customers that didn't place an order in the last seven days.
Try adding this at the and of your query :
having max(orders.order_date) < dateadd(day, -7, getdate())
You can try a
Datediff(dd,<datecolumn>,getdate())
and use
>= 7
as a condition.
The query that you want should look like this:
SELECT c.customer_id, c.customer_name, a.help_desk_agent,
Max(orders.order_date)
FROM customers c JOIN
(SELECT o.customer_id, MAX(o.order_date) as max_order_date
FROM orders o
GROUP BY o.customer_id
) o
ON c.customer_id = o.customer_id
WHERE c.help_desk_agent_id = 111 AND
o.max_order_date < dateadd(day, -7, getdate());
Your query has multiple issues:
The alias customers.customer_id is not understood in the subquery.
The select distinct is unnecessary.
LEFT JOIN is unnecessary because presumably customers have at least one order and you require a match to the agent table.
You don't need the agent table, because the information you want is in the customer table.

Oracle SQL - Getting Max Date with several joins

I think I may be overthinking this, but I'm having an issue when trying to find a max date with several joins in an Oracle database as well as several where clauses. I've found many examples of simple max queries, but nothing specifically like this. The query works fine if I add a line in to find all records above a specific date (there are only a handful of results returned). However, I want to automatically get the most recent date for all records from the bill table. This database has an additional table where the actual bill amount is stored, so that adds another layer.
SELECT p.purchase_id, p.account_id, b.bill_date, bp.current_amount
FROM Purchases p
JOIN Bill_Purchases bp ON p.purchase_id = bp.purchase_id
JOIN Bills b ON bp.bill_id = b.bill_id
--NEED TO GET MOST RECENT DATE FROM BILL TABLE FOR EACH BILL
WHERE p.type != 'CASH'
AND bp.amount > '100.00'
AND p.status = 'Approved'
AND p.purchase_id IN ( ... list of purchases ...);
I have tried doing subqueries with Max functions in them, but am not having any luck. Each query returns the same amount of records as the original query. How would I rearrange this query to still retrieve all of the necessary columns and where clauses while still limiting this to only the most recent purchase that was billed?
Try like below this
SELECT p.purchase_id, p.account_id, b.bill_date, bp.current_amount
FROM Purchases p
JOIN Bill_Purchases bp ON p.purchase_id = bp.purchase_id
JOIN ( SELECT bill_id, MAX(bill_date) bill_date
FROM Bills
GROUP BY bill_id
)b ON bp.bill_id = b.bill_id
WHERE p.type != 'CASH'
AND bp.amount > '100.00'
AND p.status = 'Approved'
AND p.purchase_id IN ( ... list of purchases ...);
You may want to solve this problem using Rank() function,
SELECT p.purchase_id, p.account_id, , bp.current_amount, RANK() OVER ( partition by b.bill_id order by b.bill_date) as max_bill_date
FROM Purchases p
JOIN Bill_Purchases bp ON p.purchase_id = bp.purchase_id
JOIN Bills b ON bp.bill_id = b.bill_id
--NEED TO GET MOST RECENT DATE FROM BILL TABLE FOR EACH BILL
WHERE max_bill_date = 1
AND p.type != 'CASH'
AND bp.amount > '100.00'
AND p.status = 'Approved'`enter code here`
AND p.purchase_id IN ( ... list of purchases ...);
Do either of these work for you?
WITH data as (
SELECT
p.purchase_id, p.account_id, b.bill_date, bp.current_amount,
FROM
Purchases p
INNER JOIN Bill_Purchases bp ON p.purchase_id = bp.purchase_id
INNER JOIN Bills b ON b.bill_id = bp.bill_id
WHERE p.type <> 'CASH'
AND bp.amount > '100.00'
AND p.status = 'Approved'
AND p.purchase_id IN ( ... list of purchases ...)
), most_recent as (
SELECT max(bill_date) as bill_date FROM data
)
SELECT *
FROM data
WHERE bill_date = (select bill_date from most_recent);
SELECT purchase_id, account_id, bill_date, current_amount
FROM (
SELECT
p.purchase_id, p.account_id, b.bill_date, bp.current_amount,
dense_rank() over (order by b.bill_date desc) as dr
FROM
Purchases p
INNER JOIN Bill_Purchases bp ON p.purchase_id = bp.purchase_id
INNER JOIN Bills b ON b.bill_id = bp.bill_id
WHERE p.type <> 'CASH'
AND bp.amount > '100.00'
AND p.status = 'Approved'
AND p.purchase_id IN ( ... list of purchases ...)
) data
WHERE dr = 1;
I got this working pretty much right after posting, was a stupid mistake on my part. Data for the max function needed to be joined on the account_id, not the bill_id.

Query very slow with LEFT AND RIGHT JOIN - SQL Server CE

I need to query the payment records with between date. The payment table has relation with invoice table while the invoice table has relation with customer table. So all the payment records must be shown even the invoice or the customer record already deleted.
To get less than 50 rows with 1 month period, it will take time more than 1 minutes. Any ideas how to reduce the times?
Here my snippet code :
SELECT
C.name, I.id as id, P.amount, P.date
FROM
tbl_customer C
LEFT JOIN
tbl_inv I ON C.id = I.id_customer
RIGHT JOIN
tbl_payment P ON I.id = P.id_invoice
WHERE
P.date >= '20131201' AND P.date <= '20140101'
ORDER BY
P.date;

sql min date returning more than 1 record

I am trying to return 1 record per customer, along with the first record from another table (product). The tables are joined with an intersection table, and the date that I am using the min (date) on is a user input date.
My sql would work fine except i have noticed there a few customer which have more than one record in the product table with the same date, so they are being returned more than once. I want to just be able to return 1 product per customer. Database is oracle so I have tried using rownum but then only returning 1 record for the whole query so i'm obviously not using it correctly. This is my sql
SELECT cust.ROW_ID prod.NAME, prod.DATE
FROM cust INNER JOIN ProdCust on cust.ROW_ID = ProdCust.CUST_ID
INNER JOIN prod on ProdCust .PROD_ID = Prod.ROW_ID
INNER JOIN
(SELECT ProdCust.CUST_ID, MIN (Prod.DATE) minDate
FROM ProdCust, Prod
WHERE ProdCust.PROD_ID = Prod.ROW_ID
GROUP BY CUST_ID
) ProdCustMin on ProdCust.CUST_ID = ProdCustMin.CUST_ID AND prod.DATE = ProdCustMin.minDate
In Oracle, you can use row_number() to resolve ties:
SELECT c.ROW_ID
, p.NAME
, p.DATE
FROM Cust c
JOIN (
SELECT row_number() over (partition by pc.CUST_ID order by p.DATE) rn
, pc.CUST_ID
, p.NAME
, p.DATE
FROM Prod p
JOIN ProdCust pc
ON pc.PROD_ID = p.ROW_ID
) p
ON c.ROW_ID = p.CUST_ID
AND p.rn = 1 -- First row only

Segment purchases based on new vs returning

I'm trying to write a query that can select a particular date and count how many of those customers have placed orders previously and how many are new. For simplicity, here is the table layout:
id (auto) | cust_id | purchase_date
-----------------------------------
1 | 1 | 2010-11-15
2 | 2 | 2010-11-15
3 | 3 | 2010-11-14
4 | 1 | 2010-11-13
5 | 3 | 2010-11-12
I was trying to select orders by a date and then join any previous orders on the same user_id from previous dates, then count how many had orders, vs how many didnt. This was my failed attempt:
SELECT SUM(
CASE WHEN id IS NULL
THEN 1
ELSE 0
END ) AS new, SUM(
CASE WHEN id IS NOT NULL
THEN 1
ELSE 0
END ) AS returning
FROM (
SELECT o1 . *
FROM orders AS o
LEFT JOIN orders AS o1 ON ( o1.user_id = o.user_id
AND DATE( o1.created ) = "2010-11-15" )
WHERE DATE( o.created ) < "2010-11-15"
GROUP BY o.user_id
) AS t
Given a reference data (2010-11-15), then we are interested in the number of distinct customers who placed an order on that date (A), and we are interested in how many of those have placed an order previously (B), and how many did not (C). And clearly, A = B + C.
Q1: Count of orders placed on reference date
SELECT COUNT(DISTINCT Cust_ID)
FROM Orders
WHERE Purchase_Date = '2010-11-15';
Q2: List of customers placing order on reference date
SELECT DISTINCT Cust_ID
FROM Orders
WHERE Purchase_Date = '2010-11-15';
Q3: List of customers who placed an order on reference date who had ordered before
SELECT DISTINCT o1.Cust_ID
FROM Orders AS o1
JOIN (SELECT DISTINCT o2.Cust_ID
FROM Orders AS o2
WHERE o2.Purchase_Date = '2010-11-15') AS c1
ON o1.Cust_ID = c1.Cust_ID
WHERE o1.Purchase_Date < '2010-11-15';
Q4: Count of customers who placed an order on reference data who had ordered before
SELECT COUNT(DISTINCT o1.Cust_ID)
FROM Orders AS o1
JOIN (SELECT DISTINCT o2.Cust_ID
FROM Orders AS o2
WHERE o2.Purchase_Date = '2010-11-15') AS c1
ON o1.Cust_ID = c1.Cust_ID
WHERE o1.Purchase_Date < '2010-11-15';
Q5: Combining Q1 and Q4
There are several ways to do the combining. One is to use Q1 and Q4 as (complicated) expressions in the select-list; another is to use them as tables in the FROM clause which don't need a join between them because each is a single-row, single-column table that can be joined in a Cartesian product. Another would be a UNION, where each row is tagged with what it calculates.
SELECT (SELECT COUNT(DISTINCT Cust_ID)
FROM Orders
WHERE Purchase_Date = '2010-11-15') AS Total_Customers,
(SELECT COUNT(DISTINCT o1.Cust_ID)
FROM Orders AS o1
JOIN (SELECT DISTINCT o2.Cust_ID
FROM Orders AS o2
WHERE o2.Purchase_Date = '2010-11-15') AS c1
ON o1.Cust_ID = c1.Cust_ID
WHERE o1.Purchase_Date < '2010-11-15') AS Returning_Customers
FROM Dual;
(I'm blithely assuming MySQL has a DUAL table - similar to Oracle's. If not, it is trivial to create a table with a single column containing a single row of data. Update 2: bashing the MySQL 5.5 Manual shows that 'FROM Dual' is supported but not needed; MySQL is happy without a FROM clause.)
Update 1: added qualifier 'o1.Cust_ID' in key locations to avoid 'ambiguous column name' as indicated in the comment.
How about
SELECT * FROM
(SELECT * FROM
(SELECT CUST_ID, COUNT(*) AS ORDER_COUNT, 1 AS OLD_CUSTOMER, 0 AS NEW_CUSTOMER
FROM ORDERS
GROUP BY CUST_ID
HAVING ORDER_COUNT > 1)
UNION ALL
(SELECT CUST_ID, COUNT(*) AS ORDER_COUNT, 0 AS OLD_CUSTOMER, 1 AS NEW_CUSTOMER
FROM ORDERS
GROUP BY CUST_ID
HAVING ORDER_COUNT = 1)) G
INNER JOIN
(SELECT CUST_ID, ORDER_DATE
FROM ORDERS) O
USING (CUST_ID)
WHERE ORDER_DATE = [date of interest] AND
OLD_CUSTOMER = [0 or 1, depending on what you want] AND
NEW_CUSTOMER = [0 or 1, depending on what you want]
Not sure if that'll do the whole thing, but it might provide a starting point.
Share and enjoy.
select count(distinct o1.cust_id) as repeat_count,
count(distinct o.cust_id)-count(distinct o1.cust_id) as new_count
from orders o
left join (select cust_id
from orders
where purchase_date < "2010-11-15"
group by cust_id) o1
on o.cust_id = o1.cust_id
where o.purchase_date = "2010-11-15"