Get count of repeat visits - sql

I have a very simple question that's doing my head in, so I'm turning to stackoverflow for it to be cleared.
I have some category ids on a website, and user_ids. A user_id can visit a category several time, as is shown in the example below.
I want my query to return the number of repeat visitors per category (=visited more than once): e.g. for category 113 it's 2 repeat visitors (user_ids 6 and 5 visited/occur more than once) over a total of 7 total visitors. The idea is to calculate a repeat visit rate (here would be 2/7).
Somehow, my mind is stumped about this. Would appreciate any help, thanks :)
category_id
user_id
113
6
113
6
113
5
113
5
113
1
113
7
113
6
120
11
120
11
120
9
Many thanks!

You can use two levels of aggregation:
select category_id,
sum(case when cnt > 1 then 1 else 0 end) as num_repeat_users
from (select category_id, user_id, count(*) as cnt
from t
group by category_id, user_id
) cu
group by category_id;

Related

SQL Coding for Logical Operators question

Q:
Using the retail. Inventory table, calculate what the total inventory would be for each product with 5 or less items in stock if all of the items on order were delivered immediately without any other sales taking place. The product_id for each product should be the first column in your results.
I need a sql code the one I am using is not working and I dont know another way to get the result my professor is asking for.
Expected:
product_id ?column?
3 20
4 37
6 13
8 4
10 24
24 41
31 4
41 35
42 2
58 25
71 28
this is what I am using, but like I said it's wrong:
select product_id
from retail.inventory
where total_on_hand <= 5;
SELECT product_id, SUM(quantity_on_hand + quantity_on_order) AS total_inventory
FROM retail.inventory
WHERE quantity_on_hand + quantity_on_order <= 5
GROUP BY product_id
This query first selects the product_id and calculates the total inventory for each product by adding the quantity_on_hand and quantity_on_order columns. It then filters the results to only include products with 5 or less items in stock, and finally groups the results by product_id to calculate the total inventory for each product.
SELECT product_id, SUM(quantity_on_hand + quantity_on_order) AS total_inventory_name
FROM retail_inventory_tablename
WHERE quantity_on_hand <= 5
GROUP BY product_id
You may try above
Took this class - the answer is:
SELECT product_id, total_on_hand + total_on_order
FROM retail.inventory
WHERE total_on_hand <= 5

Return rows where specific number is reached for the first time (postgres)

Have hit a roadblock.
Context: am using PostgreSQL 9.5.8
I have a table, as follows, with customers' points accrued. The table has multiple rows per customer as it records every change in points (like an event table). i.e. customer 1 may buy 1 item and accrue 10 points which is one row, then on another day spend some of these points and be left with 5 points which is another row, and then purchase another item and accrue a further 10 bringing them back up to 15 which displays as another row. Each of these rows with point amounts has a created_at column.
Example table:
Customer ID created_at no_points row
123 17/09/2017 5 1
123 09/10/2017 8 2
124 10/10/2017 12 3
123 10/10/2017 15 4
125 12/10/2017 12 5
126 17/09/2017 6 6
123 11/10/2017 11 7
123 12/10/2017 9 8
127 17/09/2017 5 9
124 11/10/2017 5 10
125 13/10/2017 5 11
123 13/10/2017 12 12
I want to track the first time a customer reaches a certain threshold i.e. >= 10 points. It doesn't matter how much they go over 10 points, the only criteria is that I select the first time the customer reaches this threshold. I would also like this query to fetch only rows where the customer has reached the threshold of 10 for the first time in the last week.
Following these rules, in the above example, I would like my query to select rows 3, 4 and 5.
I have tried the following query:
SELECT x.id,
min(x.created_at)
FROM (
SELECT
p.id as id,
p.created_at as created_at,
p.amount as amount
FROM "points" p
WHERE p.amount >= 10 ) x
WHERE x.created_at >= (now()::date - 7)
AND x.created_at < now()::date
GROUP BY x.id
I'm unsure that I'm retrieving the right thing however from the result set I am seeing & the results set is huge so it's not evident. Could someone sense check?
Thanks in advance.
Use cumulative functions:
select p.*
from (select p.*,
sum(num_points) over (partition by p.customer_id order by p.created_at) as cume_num_points
from points p
) p
where cume_num_points >= 10 and
(cume_num_points - num_points) < 10;
EDIT:
I may have misunderstood the question. If you just want the first break, one method uses window functions:
select p.*
from (select p.*,
lag(num_points) over (partition by p.customer_id order by p.created_at) as prev_num_points
from points p
) p
where num_points >= 10 and
prev_num_points < 10;
Or, without a subquery:
select distinct on (p.customer_id) p.*
from customers p
where num_points >= 10
order by p.customer_id, p.created_at;

Get the latest price SQLITE

I have a table which contain _id, underSubheadId, wefDate, price.
Whenever a product is created or price is edited an entry is made in this table also.
What I want is if I enter a date, I get the latest price of all distinct UnderSubheadIds before the date (or on that date if no entry found)
_id underHeadId wefDate price
1 1 2016-11-01 5
2 2 2016-11-01 50
3 1 2016-11-25 500
4 3 2016-11-01 20
5 4 2016-11-11 30
6 5 2016-11-01 40
7 3 2016-11-20 25
8 5 2016-11-15 52
If I enter 2016-11-20 as date I should get
1 5
2 50
3 25
4 30
5 52
I have achieved the result using ROW NUMBER function in SQL SERVER, but I want this result in Sqlite which don't have such function.
Also if a date like 2016-10-25(which have no entries) is entered I want the price of the date which is first.
Like for 1 we will get price as 5 as the nearest and the 1st entry is 2016-11-01.
This is the query for SQL SERVER which is working fine. But I want it for Sqlite which don't have ROW_NUMBER function.
select underSubHeadId,price from(
select underSubHeadId,price, ROW_NUMBER() OVER (Partition By underSubHeadId order by wefDate desc) rn from rates
where wefDate<='2016-11-19') newTable
where newTable.rn=1
Thank You
This is a little tricky, but here is one way:
select t.*
from t
where t.wefDate = (select max(t2.wefDate)
from t t2
where t2.underSubHeadId = t.underSubHeadId and
t2.wefdate <= '2016-11-20'
);
select underHeadId, max(price)
from t
where wefDate <= "2016-11-20"
group by underHead;

How do I create a frequency distribution?

I'm trying to create a frequency distribution to show how many customers have transacted 1x, 2x, 3x, etc.
I have a database transactions and column user_id. Each row indicates a transaction, and if a user_id shows up in multiple rows, that user has done multiple transactions.
Now I'd like to get a list that looks something like this:
Tra. | Freq.
0 | 345
1 | 543
2 | 45
3 | 20
4 | 0
5 | 3
etc
Currently I have this, but it just shows a list of users and how many transactions they have had.
SELECT user_id, COUNT(user_id) as number_of_transactions
FROM transactions
GROUP BY user_id
ORDER BY number_of_transactions DESC;
I did some digging and was suggested that generate_series might help, but I'm stuck and don't know how to move forward.
Use the first result as input to an outer query where you apply the count again, but this time grouping on number_of_transactions:
SELECT number_of_transactions, COUNT(*) AS freq
FROM (
SELECT user_id, COUNT(user_id) as number_of_transactions
FROM transactions
GROUP BY user_id
) A
GROUP BY number_of_transactions;
This would transform a result like:
user_id number_of_transactions
----------- ----------------------
1 2
2 1
3 2
4 4
to this:
number_of_transactions freq
---------------------- -----------
1 1
2 2
4 1

SQL query: count for all the reviews that each customer has written

Lets say I have one table called "REVIEWS"
This table has Reviews that customers have written for various products.
I would like to be able to get a "count" for all the reviews that each customer has written,
so I write:
SELECT count(*) AS counter
FROM reviews
WHERE customers_id = 12345
Now, my problem is that I wish to have a count like above BUT only for customers who have written a SPECIFIC product review
For instance,
SELECT customers_review
FROM reviews
WHERE
products_id = '170'
In summary, I wish to be able to get the customers TOTAL COUNT for every review they have written, but ONLY for customers who have written a review for a specific product.
select customers_id, count(*)
from reviews
where customers_id in
(select customers_id from reviews where products_id = '170')
group by customers_id;
SELECT customers_id, COUNT(*) AS counter
FROM reviews
WHERE customers_id IN(
SELECT customers_id
FROM reviews
WHERE
products_id = '170'
)
GROUP BY customers_id
This will pull any customer who wrote about product X, and then count up total number of reviews they posted.
Select customer_ID, Count(*)
FROM reviews
WHERE customer_ID in ( Select Customer_ID from reviews where products_id = '170')
Group By customer_ID
This should give you a list of all CustomerID's along with the Count of all their reviews, but it will limit it only to the customers who have left a review for product 170.
Just add to the end of your first query
WHERE EXISTS (
SELECT 1 FROM reviews AS r
WHERE r.customers_id = reviews.customers_id
AND product_id = '170')
GROUP BY reviews.customers_id
Not disagreeing with the approaches in this thread. But I think windowing/analytic SQL offer another way of looking at this problem. You can get the counts:
Count of Reviews by Customer on this
detail row
Count of Reviews By
product on this detail row
Count of
Reviews by this customer on this
product on this detail row
Any
details (the text of the review, the
review ID etc...)
SQL Statement
SELECT
revid,
CUSTID,
PRODID,
COUNT (*) OVER (PARTITION BY custid) ByCust,
COUNT (*) OVER (PARTITION BY prodid) ByProd,
COUNT (*) OVER (PARTITION BY prodid, custid) ByCustProd
FROM customer_reviews
ORDER BY custid, prodid
--OUTPUT--
1520 106 1900 16 604 3
4650 106 1900 16 604 3
2730 106 1900 16 604 3
4640 106 3900 16 254 1
6287 110 1900 28 604 2
5849 110 1900 28 604 2
5965 110 3900 28 254 2
6117 110 3900 28 254 2