Return value in Access query - sql

I need help with a query in Access. I need to return a customer name who has three or more orders within the past 14 days as of today's date that are still active. It also should display the orderdates in the results. This will populate on a report and grouped by the "cusname" and show each "orderdate". I tried using the query wizard and entering in the below sql but it populates no results. Can someone please help?
Select customerid, count(*), cusname,orderdate,orderstatus
From tablename
Where orderstatus="active"
Group by customerid,cusname,orderdate,orderstatus
Having Count(*) >=3;
Table:
CusName:|orderdate:
Mary 4/4/2021
Mary 4/3/2021
Mary 4/8/2021
Mary 3/23/2021
Bob 4/9/2021
Bob 4/1/2021
What I expect the result to be :
Table:
Customerid:|CusName:|orderdate:
1 Mary 4/4/2021
1 Mary 4/3/2021
1 Mary 4/8/2021

you should put filter on date as well
you probably shouldn't group by order date, if you're trying to count the "unique orders per customer" but not "unique orders per customer per date"
SELECT customer_id, cusname, COUNT(*)
FROM <tablename>
WHERE ABS(DateDiff('d', order_date, NOW())) <= 14 -- check order date
AND order_status = 'active' -- check order status
GROUP BY customer_id, cusname -- group by customer, not by order
HAVING COUNT(*) >= 3 -- filter customers with 3+ orders

This is rather tricky in MS Access, but you can use:
select t.*
from tablename as t
where t.orderstatus = "active" and
t.orderdate in (select top 3 t2.orderdate
from tablename t2
where t2.customerid = t.customerid and
t2.orderstatus = "active" and
t2.orderdate > dateadd("day", -14, date())
order by t2.orderdate desc
) and
3 <= (select count(*)
from tablename as t3
where t3.customerid = t.customerid and
t3.orderstatus = "active" and
t3.orderdate > dateadd("day", -14, date())
);
The first subquery gets the most recent three rows for each customer. The second checks that there are at least three.

Try this
SELECT t.customerid,
t.cusname,
t.orderdate,
t.orderstatus
FROM tablename AS t
WHERE t.orderstatus = "active"
AND t.orderdate > Dateadd("d", -14, DATE())
AND (SELECT Count(t1.cusname)
FROM tablename AS t1
WHERE t.customerid = t1.customerid
AND t1.orderstatus = "active"
AND t1.orderdate > Dateadd("d", -14, DATE())) >= 3

Related

Fill in blank dates for rolling average - CTE in Snowflake

I have two tables – activity and purchase
Activity table:
user_id date videos_watched
1 2020-01-02 3
1 2020-01-04 5
1 2020-01-07 5
Purchase table:
user_id purchase_date
1 2020-01-01
2 2020-02-02
What I would like to do is to get a 30 day rolling average since purchase on how many videos has been watched.
The base query is like this:
SELECT
DATEDIFF(DAY, p.purchase_date, a.date) AS day_since_purchase,
AVG(A.VIDEOS_VIEWED)
FROM PURCHASE P
LEFT OUTER JOIN ACTIVITY A ON P.USER_ID = A.USER_ID AND
A.DATE >= P.PURCHASE_DATE AND A.DATE <= DATEADD(DAY, 30, P.PURCHASE_DATE)
GROUP BY 1;
However, the Activity table only has records for each day a video has been logged. I would like to fill in the blanks for days a video has not been viewed.
I have started to look into using a CTE like this:
WITH cte AS (
SELECT date('2020-01-01') as fdate
UNION ALL
SELECT CAST(DATEADD(day,1,fdate) as date)
FROM cte
WHERE fdate < date('2020-04-01')
) select * from cte
cross join purchases p
left outer join activity a
on p.user id = a.user_id
and a.fdate = p.purchase_date
and a.date >= p.purchase_date and a.date <= dateadd(day, 30, p.purchase_date)
The end goal is to have something like this:
days_since_purchase videos_watched
1 3
2 0 --CTE coalesce inserted value
3 0
4 5
Been trying for the last couple of hours to get it right, but still can't really get the hang of it.
If you want to fill in the gaps in the result set, then I think you should be generating integers rather than dates:
WITH cte AS (
SELECT 1 as day_since_purchase
UNION ALL
SELECT 1 + day_since_purchase
FROM cte
WHERE day_since_purchase < 4
)
SELECT cte.day_since_purchase, COALESCE(avg_videos_viewed, 0)
FROM cte LEFT JOIN
(SELECT DATEDIFF(DAY, p.purchase_date, a.date) AS day_since_purchase,
AVG(A.VIDEOS_VIEWED) as avg_videos_viewed
FROM purchases p JOIN
activity a
ON p.user id = a.user_id AND
a.fdate = p.purchase_date AND
a.date >= p.purchase_date AND
a.date <= dateadd(day, 30, p.purchase_date)
GROUP BY 1
) pa
ON pa.day_since_purchase = cte.day_since_purchase;
You can use a recursive query to generate the 30 days following each purchase, then bring the activity table:
with cte as (
select
purchase_date,
client_id,
0 days_since_purchase,
purchase_date dt
from purchases
union all
select
purchase_date,
client_id,
days_since_purchase + 1
dateadd(day, days_since_purchase + 1, purchase_date)
from cte
where days_since_purchase < 30
)
select
c.days_since_purchase,
avg(colaesce(a. videos_watch, 0)) avg_ videos_watch
from cte c
left join activity a
on a.client_id = c.client_id
and a.fdate = c.purchase_date
and a.date = c.dt
group by c.days_since_purchase
Your question is unclear on whether you have a column in the activity table that stores the purchase date each row relates to. Your query has column fdate but not your sample data. I used that column in the query (without such column, you might end up counting the same activity in different purchases).

Limiting records by included/excluded/null date ranges

First, a description of my task. I need to identify customers that have placed orders within the past 2 years. However, I need a subset of those records.
There needs to be 1 or more orders placed between 12-24 months ago.
A gap where NO orders are placed between 1-12 months ago.
1 or more new orders have been placed within the past month.
Sounds easy enough, but I've spent way too much time isolating the constraints without receiving the desired output.
Here's my current code attempt:
SELECT * FROM
(SELECT CUSTOMER_ID AS "CUSTOMER", NAME, DATE_ENTERED,
ROW_NUMBER() OVER(PARTITION BY CUSTOMER_ID
ORDER BY DATE_ENTERED desc) SEQ
FROM
A_ATEST
WHERE
DATE_ENTERED >= ADD_MONTHS(TRUNC(sysdate),-24) AND
(DATE_ENTERED >= ADD_MONTHS(TRUNC(sysdate),-1) AND
DATE_ENTERED < ADD_MONTHS(TRUNC(sysdate),-12)) AND
NOT EXISTS(SELECT null FROM A_ATEST WHERE
DATE_ENTERED < ADD_MONTHS(TRUNC(sysdate),-1) AND
DATE_ENTERED > ADD_MONTHS(TRUNC(sysdate),-12))
) a
WHERE
(SEQ = 1 AND
DATE_ENTERED >= ADD_MONTHS(TRUNC(sysdate),-1)) AND
(SEQ = 2 AND
DATE_ENTERED < ADD_MONTHS(TRUNC(sysdate),-12))
SAMPLE DATA: (I don't see a way to add a table, so here goes...)
CUSTOMER, NAME, DATE_ENTERED
100 A 08-APR-20
100 A 01-MAR-20
100 A 01-MAR-20
101 B 09-MAR-20
101 B 07-MAR-19
101 B 01-MAR-19
102 C 04-APR-20
102 C 03-JAN-19
102 C 05-JAN-18
Ideally, the result set from my current code should display:
CUSTOMER, NAME, DATE_ENTERED, SEQ
102 C 04-APR-20 1
102 C 03-JAN-19 2
I'm not married to my code as it is. I'm hoping someone can lead me to a better way to approach this task.
Thanks!
-dougbert
I think this will give you what you want. Your question says you want the list of customers, but your output data suggests you want a list of orders from those customers.
SELECT CUSTOMER_ID AS "CUSTOMER", NAME, DATE_ENTERED,
FROM A_ATEST a1
WHERE a1.DATE_ENTERED >= ADD_MONTHS(TRUNC(sysdate),-24)
AND EXISTS ( SELECT 1 FROM A_ATEST a3
WHERE a3.customer_id = a1.customer_id
AND a3.DATE_ENTERED BETWEEN ADD_MONTHS(TRUNC(sysdate), -24)
AND ADD_MONTHS(TRUNC(sysdate), -12))
AND NOT EXISTS ( SELECT 1 FROM A_ATEST a2
WHERE a2.customer_id = a1.customer_id
AND DATE_ENTERED < ADD_MONTHS(TRUNC(sysdate), -1)
AND DATE_ENTERED > ADD_MONTHS(TRUNC(sysdate), -12))
AND EXISTS ( SELECT 1 FROM A_ATEST a4
WHERE a4.customer_id = a1.customer_id
AND a4.DATE_ENTERED > ADD_MONTHS(TRUNC(sysdate), -12))
The key here is that your subqueries need to correlate customer_id back to the outermost A_ATEST table. The way you had it written basically meant "and there exists an order from any customer between 1 and 12 months ago".
You need orders in the last two years with a gap of one year. That suggests lag():
select a.*
from (select a.*,
max(case when prev_de < add_months(date_entered, -12) then 1 else 0 end) over (partition by customer_id) as has_12month_gap
from (select a.*,
lag(date_entered) over (partition by CUSTOMER_ID order by date_entered) as prev_de,
max(date_entered) over (partition by customer_id) as max_de
from A_ATEST a
where date_entered > add_months(sysdate, -24)
) a
) a
where max_de > add_months(sysdate, -1) and
has_12month_gap = 1;
EDIT:
The above brings back all the transactions. For the customers only, it is similar logic but a little simpler:
select customer
from (select a.*,
lag(date_entered) over (partition by CUSTOMER_ID order by date_entered) as prev_de
from A_ATEST a
where date_entered > add_months(sysdate, -24)
) a
group by customer
where max(date_entered) > add_months(sysdate, -1) and
max(case when prev_de < add_months(date_entered, -12) then 1 else 0 end) = 1;
In case anyone references my question in the future, I wanted to share my final production solution. So, there a number of changes to get the output I required.
SELECT DISTINCT a1.CUSTOMER_NO AS "CUSTOMER", ci.NAME, MAX(a1.DATE_ENTERED) AS "ORDER DATE", a1.SALESMAN_CODE AS "SALESPERSON"
FROM CUSTOMER_INFO ci LEFT JOIN CUSTOMER_ORDER a1 ON ci.CUSTOMER_ID = a1.CUSTOMER_NO
WHERE a1.DATE_ENTERED >= ADD_MONTHS(TRUNC(sysdate), -1)
AND EXISTS ( SELECT 1 FROM CUSTOMER_ORDER a3
WHERE a3.customer_no = a1.customer_no
AND a3.DATE_ENTERED BETWEEN ADD_MONTHS(TRUNC(sysdate), -24)
AND ADD_MONTHS(TRUNC(sysdate), -12))
AND NOT EXISTS ( SELECT 1 FROM CUSTOMER_ORDER a2
WHERE a2.customer_no = a1.customer_no
AND DATE_ENTERED < ADD_MONTHS(TRUNC(sysdate), -1)
AND DATE_ENTERED > ADD_MONTHS(TRUNC(sysdate), -12))
AND EXISTS ( SELECT 1 FROM CUSTOMER_ORDER a4
WHERE a4.customer_no = a1.customer_no
AND a4.DATE_ENTERED > ADD_MONTHS(TRUNC(sysdate), -24))
GROUP BY a1.CUSTOMER_NO, ci.NAME, a1.SALESMAN_CODE
ORDER BY a1.CUSTOMER_NO, "ORDER DATE"
Thanks again to both eaolson and Gordon Linoff for your help in getting me to where I needed to go.

sql query - find distinct user from table

I am trying to solve a problem using SQL query and need some expert's advice.
I have below transaction table.
-- UserID, ProductId, TransactionDate
-- 1 , 2 , 2014-01-01
-- 1 , 3 , 2014-01-05
-- 2 , 2 , 2014-01-02
-- 2 , 3 , 2014-05-07
.
.
.
What I am trying to achieve is to find all user who purchased more than one product WITHIN 30 DAYS .
My query so far is like
select UserID, COUNT(distinct ProductID)
from tableA
GROUP BY UserID HAVING COUNT(distinct ProductID) > 1
I am not sure where to apply "WITH IN 30 DAYS" logic in the query .
The outcome should be :
1, 2
2, 1
Thanks in advance for your help.
Edit: Within 30 Days
SQL Fiddle
SELECT
a.UserID,
COUNT(DISTINCT ProductID)
FROM TableA a
INNER JOIN (
SELECT UserID, TransactionDate = MAX(TransactionDate)
FROM TableA
GROUP BY UserID
) AS t
ON t.UserID = a.UserID
AND a.TransactionDate >= DATEADD(DAY, -30, t.TransactionDate)
AND a.TransactionDate <= t.TransactionDate
GROUP BY a.UserID
You can use GROUP BY YEAR(TransactionDate), MONTH(TransactionDate)
SELECT
UserID,
COUNT(DISTINCT ProductID)
FROM TableA
GROUP BY
UserID, YEAR(TransactionDate), MONTH(TransactionDate)
HAVING
COUNT(DISTINCT ProductID) > 1
Just add a where clause.
SELECT UserID, COUNT(DISTINCT ProductID) cnt
FROM tableA
WHERE TransactionDate >= CAST(DATEADD(DAY,-30,GETDATE()) AS DATE)
GROUP BY UserID
HAVING COUNT(DISTINCT ProductID) > 1
This works because the where clause is performed BEFORE the Group By and Having. So first it filters out all transactions over 30 days old and then returns only people who bought two distinct products.
Query Processing Order:
http://blog.sqlauthority.com/2009/04/06/sql-server-logical-query-processing-phases-order-of-statement-execution/

How to run a subquery based on results of a query SQL

I have 2 queries i'd like to run. The idea here is to run a query on the transaction table by the transaction "type". Based on these results, I want to run another query to see the customers last transaction based on a specific type to see if the service ID was the same. If it's not the same, I want to flag it as "upgraded"
Here is the initial query that Pulls the results from a transactions table based on a transaction type:
Select customerid, serviceid
from Transactions
where (dtcreated > #startdate and dtcreated < #enddate) and (transactiontype = 'Cust Save')
The output for this is:
Customerid ServiceID
1 11
2 21
3 21
4 11
5 12
6 11
What i'd like to do next is run this query, matching the customerID to see what the customers last charge was:
Select serviceID, MAx(dtcreated) as MostRecent
From Transactions
Where (transactiontype = 'Cust Purchase')
Group By serviceID
My Final output combining the two queries would be:
Customerid ServiceID Last Purchase Upgraded?
1 11 11 No
2 21 11 Yes
3 21 12 Yes
4 11 10 Yes
5 12 12 No
6 11 11 No
I thought this might work but it doesn't quite give me what I want. It returns too many results, so the query is obviously not correct.:
Select serviceID, MAx(dtcreated) as MostRecent
From Transactions
WHERE Where (transactiontype = 'Cust Purchase') AND EXISTS
(Select customerid, serviceid
from Transactions
where (dtcreated > #startdate and dtcreated < #enddate) and (transactiontype = 'Cust Save'))
GROUP BY serviceid
If I understand the requirements properly you can use ROW_NUMBER to determine which record is the latest per customerID. Then you can JOIN this back to the transactions table to determine if there is a match in ServiceID:
SELECT r.CustomerID,
t.ServiceID,
t.dtCreated,
Upgraded = CASE WHEN t.ServiceID = cp.ServiceID THEN 0 ELSE 1 END
FROM Transactions AS t
INNER JOIN
( SELECT CustomerID,
ServiceID,
dtCreated,
RowNumber = ROW_NUMBER() OVER(PARTITION BY CustomerID ORDER BY dtCreated DESC)
FROM Transactions
WHERE transactiontype = 'Cust Purchase'
) AS cp
ON cp.CustomerID = t.CustomerID
AND cp.RowNumber = 1
WHERE t.dtcreated > #startdate
AND t.dtcreated < #enddate
AND t.transactiontype = 'Cust Save'
I think you need something like this.
Here t1 gives you the max dtcreated per customer;
t2 contains all transactions in the date range given;
t3 gives you the last purchase per customer.
select
t1.customerid,
t3.serviceid as Last_Purchase_ServiceID,
t1.dtcreated as Last_Purchase_DateCreated,
t2.ServiceID as Current_Purchase_ServiceID,
t2.dtcreated as Current_Purchase_DateCreated
from
(
select customerid, max(dtcreated) as dtcreated
from
Transactions
group by customerid
)
t1
join
(
select customerid, serviceid, dtcreated
from Transactions
where (dtcreated > #startdate and dtcreated < #enddate) and (transactiontype = 'Cust Save')
) t2 on t1.customerid = t2.customerid
join
Transactions t3 on t1.customerid = t3.customerid and t1.dtcreated = t3.dtcreated
Try OUTER APPLY:
Select t.customerid, t.serviceid, o.MostRecent
from Transactions t
OUTER APPLY (
Select MAx(dtcreated) as MostRecent
From Transactions
Where transactiontype = 'Cust Purchase' and Customerid = t.Customerid --And ServiceID = t.ServiceID
) o
where (t.dtcreated > #startdate and t.dtcreated < #enddate) and (t.transactiontype = 'Cust Save')

sql find most recent record

I have the following query which gets a tally of records with the given criteria. The following shows the two records in the table and the results of the query. What I want is to just get the most recent occurrence based on the EntryDate column.
0 A 2012-08-09 00:00:00.000 N0206527 N0168639 NULL 09 420202 LTD False
0 A 2012-08-08 00:00:00.000 N0206527 N0168639 NULL 09 420202 LTD False
DCMNumber CustomerDivision CustomerSerial Product PriorNonDenied
N0064819 09 420202 LTD 2
SET #firstDayOfCurrentMonth = DATEADD(m,DATEDIFF(M,0,GETDATE()),0)
SELECT DCMNumber, CustomerDivision, CustomerSerial, Product, COUNT(*) AS PriorNonDenied
FROM cauAssignedClaim
WHERE RecordType = 'A' AND
EscFromSTD = '0' AND
([Status] <> 'DE' OR
[Status] IS NULL) AND
CONVERT(VARCHAR(10), EntryDate, 110) BETWEEN
CASE WHEN Product = 'LTD' THEN CONVERT(VARCHAR(10), #firstDayOfCurrentMonth, 110)
ELSE CONVERT(VARCHAR(10), DATEADD(Day, -7, GETDATE()-1), 110)
END
AND CONVERT(VARCHAR(10), GETDATE()-1, 110)
GROUP BY [Status],
DCMNumber,
CustomerDivision,
CustomerSerial,
Product
Thanks,
Brad
I think you want something like this
SELECT a.EntryDate, a.DCMNumber, CustomerDivision, CustomerSerial, Product, WhateverElse, etc
FROM cauAssignedClaim b INNER JOIN
(SELECT MAX(DCMNumber) DCMNumber, MAX(EntryDate) EntryDate
FROM cauAssignedClaim
GROUP BY DCMNumber) a
ON ( a.DCMNumber = b.DCMNumber AND a.EntryDate = b.EntryDate )
This will give you a list of all unique DCMNumbers and their latest date. Then you can JOIN that with all the rest of the data that you need. The end result will return each DCMNumber with the latest info.
If that's not what you're looking for, add more details, so we can better understand what you are after.
MySQL:
SELECT * FROM table ORDER BY EntryDate DESC LIMIT 1
MSSQL:
SELECT TOP 1 * FROM ORDER BY EntryDate DESC
Try this
Select Top 1 col1 From Table Order by MyDate DESC