Schema:
I've a task:
Print the names of the most active users who gave more than 100 responses in the first month after registration (including the day of registration). Questions asked by users, do not count. For each username, print the number of unique user_id values. Sort the result by the field with the names in lexicographic order.
SELECT
u.display_name,
COUNT(p.user_id) AS answers_count
FROM stackoverflow.users u
JOIN stackoverflow.posts p ON u.id = p.user_id
JOIN stackoverflow.post_types pt ON p.post_type_id = pt.id
WHERE pt.type = 'Answer'
AND p.creation_date <= u.creation_date + INTERVAL '1 month'
GROUP BY u.display_name
HAVING COUNT(p.id) > 100
ORDER BY u.display_name
But after comparing result with validator it says that I've too few rows. Seems like my WHERE condition is not properly set?
Related
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
);
Here's my use-case: I 'd like to filter out all records on one condition and additionally filter out some records on additional condition.
EDITED
Here's the two conditions:
all records must met condition created_at > SYSDATE + 30
records from the table users that have (share) the same id as in the table blacklist (join on u.id = b.id) where the column endpoint is equal to XYZ must have column delayed_until that meets criterion older than now. If endpoint is not equal to XYZ, only 1st condition applies.
I have come up with the following query:
SELECT * users u
left join blacklist b
ON u.id = b.id
AND b.delayed_until < SYSDATE
AND u.endpoint = 'XYZ'
WHERE u.created_at > SYSDATE + 30;
Here's my schema (simplified):
Does my query do what intended? Can it be rewritten so that it is faster? If so, how?
I think this is what you want:
SELECT *
FROM users u JOIN
blacklist b
ON u.id = b.id AND
(b.delayed_until <= SYSDATE OR
u.endpoint <> 'XYZ'
)
WHERE u.created_at > SYSDATE + 30;
Filtering on the first table in the ON clause doesn't make sense with a LEFT JOIN.
For this query, you want indexes on users(endpoint, created_at, id) and blacklist(id, delayed_until).
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
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.
I'm doing the following query and getting all rows returned, regardless of date:
SELECT DISTINCT p.name, p.category, u.f_name, t.name
FROM (
prizes p, tags t, results r
)
LEFT JOIN
users u
ON (r.user_id = u.id)
LEFT JOIN
f_tag_lookup tl
ON (tl.tag_id = t.id)
WHERE r.tag_id = t.id
AND r.date BETWEEN concat(date_format(LAST_DAY(now() - interval 1 month), '%Y-%m-'),'01') AND last_day(NOW() - INTERVAL 1 MONTH)
r.date is a datetime field. there are three rows with dates from last month and three rows with dates from this month but I'm getting all rows back?
Would appreciate any pointers. I want to return results between the first and last day of last month i.e. all results for July.
thanks,
Could you not just use the month() date function ?
e.g. use month(date) = month(now()) as the filter.
You would need to also match on year(date) = year(now()), if you had data from more than one year.
I'm not sure of the performance, but it seems more readable to me to express it that way.
SELECT DISTINCT p.name, p.category, u.f_name, t.name
FROM (prizes p, tags t, results r)
LEFT JOIN users u ON (r.user_id = u.id)
LEFT JOIN f_tag_lookup tl ON (tl.tag_id = t.id)
WHERE r.tag_id = t.id AND
month(r.date) = month(now()) AND
year(r.date) = year(now())
figured it out. The SQL regarding dates was fine but the JOINS were wrong. Thanks for the replies.