sql query - find distinct user from table - sql

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/

Related

Return value in Access query

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

How to get 1 record on the basis of two column values in a single table?

The query is
select distinct b.UserID , cast(b.entrytime as date) ,count(*) as UserCount
from [dbo].[person] as a
join [dbo].[personcookie] as b
on a.UserID = b.UserID
where cast (b.entrytime as date) >= '08/21/2020'
and cast (b.leavetime as date) <= '08/27/2020' and a.distinction = 99
group by cast(b.entrytime as date), b.UserID
If same UserID has count more than 1 for same date, It should consider as 1. Now as it is shown in the image that USERID 10 has count 1 for 2020-08-26 and USERID 10 has count 2 for '2020-08-27'. It should show that user ID 10 has total count 2 for `2020-08-26 and 2020-08-27' (because for 2020-08-27 the count should be 1) as per the requirement.
I have added the image of tables and what output i want
It seems you want one result row per user, so group by user, not by user and date. You want to count dates per user, but each day only once. This is a distinct count.
select
p.userid,
count(distinct cast(pc.entrytime as date)) as date_count
from dbo.person as p
join dbo.personcookie as pc on pc.userid = p.userid
where p.distinction = 99
and pc.entrytime >= '2020-08-08'
and pc.leavetime < '2020-08-28'
group by p.userid
order by p.userid;
You seem to want dense_rank():
select p.UserID, cast(pc.entrytime as date),
dense_rank() over (partition by p.userID order by min(pc.entrytime)) as usercount
from [dbo].[person] p join
[dbo].[personcookie] pc
on pc.UserID = p.UserID
where cast(pc.entrytime as date) >= '2020-08-21' and
cast(pc.leavetime as date) <= '2020-08-27'
group by cast(pc.entrytime as date), p.UserID;
Notes:
The only "real" change is using dense_rank(), which enumerates the days for a given user.
Use meaningful table aliases, rather than arbitrary letters.
Use standard date/time constants. In SQL Server, that is either YYYYMMDD or YYYY-MM-DD.

SQL - One Table with Two Date Columns. Count and Join

I have a table (vOKPI_Tickets) that has the following columns:
|CreationDate | CompletionDate|
I'd like to get a count on each of those columns, and group them by date. It should look something like this when complete:
| Date | Count-Created | Count-Completed |
I can get each of the counts individually, by doing something like this:
SELECT COUNT(TicketId)
FROM vOKPI_Tickets
GROUP BY CreationDate
and
SELECT COUNT(TicketId)
FROM vOKPI_Tickets
GROUP BY CreationDate
How can I combine the output into one table? I should also note that this will become a View.
Thanks in advance
Simple generic approach:
select
coalesce(crte.creationdate, cmpl.CompletionDate) as theDate,
crte.cnt as created,
cmpl.cnt as completed
from
(select creationdate, count (*) as cnt from vOKPI_Tickets where creationdate is not null group by creationdate) crte
full join
(select CompletionDate, count (*) as cnt from vOKPI_Tickets where CompletionDate is not null group by CompletionDate) cmpl
on crte.creationdate = cmpl.CompletionDate
You can unpivot and aggregate. A general method is:
select dte, sum(created), sum(completed)
from ((select creationdate as dte, 1 as created, 0 as completed
from vOKPI_Tickets
) union all
(select completed as dte, 0 created, 1 as completed
from vOKPI_Tickets
)
) t
group by dte;
In SQL Server, you can use cross apply for this:
select d.dt, sum(d.is_completed) count_created, sum(d.is_completed) count_completed
from vokpi_tickets t
cross apply (values (creationdate, 1, 0), (completion_date, 0, 1)) as d(dt, is_created, is_completed)
where d.dt is not null
group by d.dt

SQLite Getting multiple results with LIMIT 1

I have the following problem.
Part of a task is to determine the visitor(s) with the most money spent between 2000 and 2020.
It just looks like this.
SELECT UserEMail FROM Visitor
JOIN Ticket ON Visitor.UserEMail = Ticket.VisitorUserEMail
where Ticket.Date> date('2000-01-01') AND Ticket.Date < date ('2020-12-31')
Group by Ticket.VisitorUserEMail
order by SUM(Price) DESC;
Is it possible to output more than one person if both have spent the same amount?
Use rank():
SELECT VisitorUserEMail
FROM (SELECT VisitorUserEMail, SUM(PRICE) as sum_price,
RANK() OVER (ORDER BY SUM(Price) DESC) as seqnum
FROM Ticket t
WHERE t.Date >= date('2000-01-01') AND Ticket.Date <= date('2021-01-01')
GROUP BY t.VisitorUserEMail
) t
WHERE seqnum = 1;
Note: You don't need the JOIN, assuming that ticket buyers are actually visitors. If that assumption is not true, then use the JOIN.
Use a CTE that returns all the total prices for each email and with NOT EXISTS select the rows with the top total price:
WITH cte AS (
SELECT VisitorUserEMail, SUM(Price) SumPrice
FROM Ticket
WHERE Date >= '2000-01-01' AND Date <= '2020-12-31'
GROUP BY VisitorUserEMail
)
SELECT c.VisitorUserEMail
FROM cte c
WHERE NOT EXISTS (
SELECT 1 FROM cte
WHERE SumPrice > c.SumPrice
)
or:
WITH cte AS (
SELECT VisitorUserEMail, SUM(Price) SumPrice
FROM Ticket
WHERE Date >= '2000-01-01' AND Date <= '2020-12-31'
GROUP BY VisitorUserEMail
)
SELECT VisitorUserEMail
FROM cte
WHERE SumPrice = (SELECT MAX(SumPrice) FROM cte)
Note that you don't need the function date() because the result of date('2000-01-01') is '2000-01-01'.
Also I think that the conditions in the WHERE clause should include the =, right?

Displaying records that have more than 5 days inbetween them

I have 2 tables,
tblCustomer
CustomerID(PK), FirstName, Surname)
tblPurchases
PurchaseID(PK), PurchaseDate, Qty, CustomerID(FK).
I want to display all the customers who purchased products after five (5) days or more since their last purchase in the following manner.
FirstName diff in days since last purchase
Alex 7
Thanks!
Try with the below query.
SELECT FirstName, DATEDIFF(DAY, t.PurchaseDate, getdate()) as 'diff in days since last purchase'
FROM tblCustomer c
JOIN (SELECT CustomerID, MAX(PurchaseDate)PurchaseDate
FROM tblPurchases
GROUP BY CustomerID )t ON c.CustomerID=t.CustomerID
WHERE DATEDIFF(DAY, PurchaseDate, getdate())>5
SELECT FirstName,
DATEDIFF(DAY, t.PurchaseDate, getdate()) 'diff in days since last purchase'
FROM tblCustomer c
JOIN (SELECT CustomerID, MAX(PurchaseDate)PurchaseDate
FROM tblPurchases
GROUP BY CustomerID )t ON c.CustomerID=t.CustomerID
WHERE DATEDIFF(DAY, t.PurchaseDate, getdate())>5
Give a row number based on the last purchase date for each CustomerId by joining both the tables.
Then find the difference in number of days between current date and PurchaseDate by using DATEDIFF.And also give the number of days difference in the WHERE clause.
Query
;WITH CTE AS(
SELECT [rn] = ROW_NUMBER() OVER(
PARTITION BY t.[CustomerID]
ORDER BY t.[PurchaseDate] DESC
), t.[CustomerID], t.[FirstName], t.[PurchaseDate]
FROM (
SELECT t1.[CustomerID], t1.[FirstName], t2.[PurchaseDate]
FROM [tblCustomer] t1
JOIN [tblPurchases] t2
ON t1.[CustomerID] = t2.[CustomerID]
)t
)
SELECT [FirstName],
DATEDIFF(DAY, [PurchaseDate], GETDATE()) AS [diff in days since last purchase]
FROM CTE
WHERE [rn] = 1
AND DATEDIFF(DAY, [PurchaseDate], GETDATE()) > 5;
;WITH T AS
(
SELECT
*,
DATEDIFF(DAY, [PurchaseDate], GETDATE()) AS DiffInDays
FROM #tblPurchases
WHERE DATEDIFF(DAY, [PurchaseDate], GETDATE()) > 5
)
SELECT
C.FirstName,
MAX(DiffInDays) AS DiffInDays
FROM T
LEFT JOIN #tblCustomer C ON T.CustomerId=C.CustomerId
GROUP BY C.FirstName