How to select objects if not exist between a period in Sqlite - sql

I like to select those users who haven't filled out a form in the last 7 days but I'm stuck. The background: I am working on an app that lists the users who have filled out the form but I wrote it in another query that works fine. Now I need to select just those users who haven't filled the form out in the last 7 days.
The query I wrote selects all the users because everyone has objects that outside the period.
How can I select just those users who haven't filled out the form in the given period but not to include all users. As you can see on the picture the user with id 1 appears two times with Yes and No.
Tha query I wrote:
SELECT DISTINCT auth_user.id,
CASE WHEN felmeres.date BETWEEN date("now", "-7 day") AND date('now')
THEN 'Yes'
ELSE 'No'
END AS period
FROM felmeres
LEFT JOIN profile ON profile.user_id = felmeres.user_name_id
ORDER BY felmeres.date DESC

You could use a join aggregation approach:
SELECT p.user_id
FROM profile p
INNER JOIN felmeres f
ON f.user_name_id = p.user_id
GROUP BY p.user_id
HAVING SUM(f.date BETWEEN date('now', '-7 day') AND date('now')) = 0;

If profile contains the users' data then it should be the left table in the LEFT join and the condition for the dates should be placed in the ON clause, so that you filter out the matching users:
SELECT p.*
FROM profile p LEFT JOIN felmeres f
ON f.user_name_id = p.user_id AND f.date BETWEEN date(CURRENT_DATE, '-7 day') AND CURRENT_DATE
WHERE f.user_name_id IS NULL;
Or, with NOT EXISTS:
SELECT p.*
FROM profile p
WHERE NOT EXISTS (
SELECT 1
FROM felmeres f
WHERE f.user_name_id = p.user_id AND f.date BETWEEN date(CURRENT_DATE, '-7 day') AND CURRENT_DATE
);

Related

How to pull the count of occurences from 2 SQL tables

I am using python on a SQlite3 DB i created. I have the DB created and currently just using command line to try and get the sql statement correct.
I have 2 tables.
Table 1 - users
user_id, name, message_count
Table 2 - messages
id, date, message, user_id
When I setup table two, I added this statement in the creation of my messages table, but I have no clue what, if anything, it does:
FOREIGN KEY (user_id) REFERENCES users (user_id)
What I am trying to do is return a list containing the name and message count during 2020. I have used this statement to get the TOTAL number of posts in 2020, and it works:
SELECT COUNT(*) FROM messages WHERE substr(date,1,4)='2020';
But I am struggling with figuring out if I should Join the tables, or if there is a way to pull just the info I need. The statement I want would look something like this:
SELECT name, COUNT(*) FROM users JOIN messages ON messages.user_id = users.user_id WHERE substr(date,1,4)='2020';
One option uses a correlated subquery:
select u.*,
(
select count(*)
from messages m
where m.user_id = u.user_id and m.date >= '2020-01-01' and m.date < '2021-01-01'
) as cnt_messages
from users u
This query would take advantage of an index on messages(user_id, date).
You could also join and aggregate. If you want to allow users that have no messages, a left join is a appropriate:
select u.name, count(m.user_id) as cnt_messages
from users u
left join messages m
on m.user_id = u.user_id and m.date >= '2020-01-01' and m.date < '2021-01-01'
group by u.user_id, u.name
Note that it is more efficient to filter the date column against literal dates than applying a function on it (which precludes the use of an index).
You are missing a GROUP BY clause to group by user:
SELECT u.user_id, u.name, COUNT(*) AS counter
FROM users u JOIN messages m
ON m.user_id = u.user_id
WHERE substr(m.date,1,4)='2020'
GROUP BY u.user_id, u.name

Finding days when users haven't created any entries

I've 2 tables: users and time_entries, time entries has a foreign key to the users table. Users may create time entries with some time amount in it. I want to write a query which could return summarized amounts of time in arbitrary dates range grouped by user and date - it's easy but I need to include also days when nobody entered any time_entry. I've tried to create an additional table called calendar with dates and left join time_entries to it but I couldn't retrieve a list of users that haven't entered any time_entry. Here is my query:
SELECT te.date, SUM(te.amount), user_name
FROM calendar c
LEFT JOIN time_entries te on c.date = te.date
RIGHT JOIN asp_net_users anu on te.user_id = anu.id
GROUP BY user_name, te.date
If you just want the days no user made any entry. you can use NOT EXISTS and a correlated subquery.
SELECT c.date
FROM calendar c
WHERE NOT EXISTS (SELECT *
FROM time_entries te
WHERE te.date = c.date);
If you want all users along with the days they haven't made any entry cross join the users and the days and then also use a NOT EXISTS.
SELECT anu.user_name,
c.date
FROM asp_net_users anu
CROSS JOIN calendar c
WHERE NOT EXISTS (SELECT *
FROM time_entries te
WHERE te.user_id = anu.id
AND te.date = c.date);
Thanks to sticky bit examples I was able to write the following query which solves my problem:
SELECT c.date, a.id, COALESCE(sum(te.amount), 0)
FROM asp_net_users a
CROSS JOIN (SELECT *
FROM calendar
WHERE date BETWEEN '2019-10-01 00:00:00'::timestamp AND '2019-10-31 00:00:00'::timestamp) c
LEFT JOIN time_entries te on a.id = te.user_id AND c.date = te.date
WHERE a.department_guid = '95b7538d-3830-48d7-ba06-ad7c51a57191'
GROUP BY c.date, a.id
ORDER BY c.date

Write SQL query that returns results for each sorted value in main column

I'm working on a project related with my college and I have the following tables:
user - table with information about registered students
payment - table with information about payments
lesson - table with list of lessons.
Possible statuses of lessons:
CONFIRMED - lesson happened successfully
SCHEDULED - for future
lessons
CANCELED - for lessons that were canceled
Each lesson appears in table only one time. Lesson status is being updated.
I need to write a SQL query that returns for each country:
number of registered users
% of users, who made their first payment in 3 days after registration
% of users, who made their first payment in 3 days after registration and had 2 confirmed
lessons in 7 days after registration
But aside from using DISTINCT to sort the table with non repeated elements, I'm stuck with the subqueries required to filter the information using each country as reference.
Perhaps you need to adapt this to your SQL dialect (mainly adding intervals on timestamps). I guess the rest should work (not tested anyways).
WITH pay3day AS (
SELECT u.id
FROM user u
INNER JOIN payment p ON p.user_id = u.id
WHERE p.datetime <= u.date_joined + INTERVAL '3 days'
GROUP BY u.id
), less2in7 AS (
SELECT u.id
FROM user u
INNER JOIN lesson l ON l.user_id = u.id
WHERE l.status = 'CONFIRMED'
AND l.datetime <= u.date_joined + INTERVAL '7 days'
GROUP BY u.id
HAVING COUNT(*) > 0
)
SELECT
COUNT(*) AS a,
(COUNT(p.id) * 100.0) / COUNT(*) AS b,
(COUNT(p.id + l.id) * 100.0) / COUNT(*) AS c
FROM user u
LEFT JOIN pay3day p ON p.id = u.id
LEFT JOIN less2in7 l ON l.id = u.id
GROUP BY u.country_code

Joining two SQL tables with different column names in Metabase

I am using Metabase to ask questions on my DB. I am trying to join two tables where the same info (user ID) has two different names. The code I wrote is as follows:
SELECT
game_states.game_module AS game, count(*)
FROM
game_states gs LEFT JOIN users u ON gs.user_id = u.id;
WHERE
games_states.state = 'after_hands'
AND
user.last_joined_stamp > now() - interval '30 days'
GROUP BY
1
ORDER BY
2 DESC
I keep getting the following error:
ERROR: invalid reference to FROM-clause entry for table "game_states" Hint: Perhaps you meant to reference the table alias "gs". Position: 127
Once you define a table alias, use it!
SELECT gs.game_module AS game, count(*)
-------^
FROM game_states gs LEFT JOIN
users u
ON gs.user_id = u.id;
WHERE gs.state = 'after_hands' AND
------^
u.last_joined_stamp > now() - interval '30 days'
------^
GROUP BY 1
ORDER BY 2 DESC;
Incidentally, you probably intend:
SELECT gs.game_module AS game, count(u.id)
FROM game_states gs LEFT JOIN
users u
ON gs.user_id = u.id AND
u.last_joined_stamp > now() - interval '30 days'
WHERE gs.state = 'after_hands'
GROUP BY 1
ORDER BY 2 DESC;
You are using LEFT JOIN, so presumably want to include all matching games -- even those with no matching users. If so, don't filter on u in the where clause and count the matches, so you can get 0.

How do combine a count in a particular select statement?

On our postgresql database we currently have 2 tables called Users and Bookings.
We are currently trying to know on the users that made a booking yesterday how many bookings they did over time.
Here is the query we have for the moment:
SELECT "domain".users.email, COUNT("domain".bookings."id")
FROM "domain".bookings
INNER JOIN "domain".users ON "domain".users."id" = "domain".bookings.user_id
GROUP BY "domain".users.email
If we have the date the booking was created (field "domain.bookings.created_at) with filter yesterday we only get the data from yesterday.
Is there a way to see on who bought yesterday how many bookings they did overtime?
Thank you!
Luca
TRY THIS
SELECT "domain".users.email, COUNT(CASE WHEN bookingdate = current_date - 1 THEN 1 END ) AS TOTAL_BOOKING
FROM "domain".bookings
INNER JOIN "domain".users ON "domain".users."id" = "domain".bookings.user_id
GROUP BY "domain".users.email
This is what you are looking for:
SELECT u.id, u.email, count(b.id) AS "Total Bookings"
FROM "domain".bookings as b
JOIN "domain".users as U ON u.id = b.user_id
WHERE EXISTS (SELECT 1
FROM "domain".bookings b2
WHERE b2.created_at = current_date - 1
AND b2.user_id = b.user_id)
GROUP BY u.email;
The exists condition will only return rows from the bookings table for users that booked something yesterday.
Unrelated, but: using a keyword like domain that requires double quotes for identifiers is not such a good idea. It would save you some trouble in the long run if you found a different name
Thank you for answering. I was probably not precise enough!
Basically if I use the following query:
SELECT
"domain".users.email,
COUNT( "domain".bookings."id") AS "Total Bookings"
FROM
"domain".bookings
INNER JOIN "domain".users ON "domain".users."id" = "domain".bookings.user_id
GROUP BY
"domain".users.email
I get the number of bookings made by a user.
BUT
I want to get the following: the number of bookings made all time by the users that bought yesterday.
I tried this
SELECT
"domain".users.email,
COUNT( "domain".bookings."id") AS "Total Bookings"
FROM
"domain".bookings
INNER JOIN "domain".users ON "domain".users."id" = "domain".bookings.user_id
WHERE
"domain".bookings.created_at = 'yesterday'
GROUP BY
"domain".users.email
But I am getting no responses...
Thank you for help!
Luca