How do combine a count in a particular select statement? - sql

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

Related

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

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
);

Get active and total bookings (from 1 table) for every user in users table

I have 2 tables:
users:
username (pk)
bookings:
username (fk)
status (A = Active, C = Cancelled , D = DONE)
I'm willing to show user details along with with their count of active and total bookings (where total bookings will be all the entries in "bookings" table for a particular user).
Table to show:
username, active bookings (count), total bookings (count)
Currently I'm unable to make an efficient query for this.
My DB is postgresql.
Please assist.
Thank you
As you are using PostgreSQL you can take the advantage of Filter() clause. Also you have to use Left Join because you want the details for every user from user table. So Write your query like below:
select
t1.username,
count(*) filter (where t2.status='A') as "Active_Bookings",
count(t2.*) as "Total_Bookings"
from users t1 left join bookings t2 on t1.username=t2.username
group by 1
Edit as per comment:
Filter clause is supported by Postgresql and SQLite. For others count with case will do the thing. Below query should work for almost every other database.
select
t1.username,
count(case when t2.status='A' then 1 end) as "Active_Bookings",
count(t2.*) as "Total_Bookings"
from users t1 left join bookings t2 on t1.username=t2.username
group by t1.username
you can use sum(case when t2.status='A' then 1 else 0 end) as "Active_Bookings" also.
You can try the below -
select u.username,count(*) as total_booking,
count(case when status='Active' then 1 end) as active_bookings
from users u join bookings b on u.username=b.username
group by u.username
Not very sure I udnerstood your question but based on the input you gave try this , this should work
select x.username,active_bookings,total_bookings from (
(select username, count(status) as active_bookings from bookings where status='A' group by username)x join (select username,count(status) as total_bookings from bookings group by username)y on x.username=y.username);

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

SQL Three table join

I haven't been able to solve this problem for several days now and I'm hoping you can help.
I'm trying to write a query that returns all the information about a stock and the last time it was updated. I would like to filter the results based on the parameter #date and return only the stocks which it's latests update is less than the supplied #date parameter. I also need the stocks with a timestamp of null so I know that theses stocks need to be updated. I have the follwing three tables that I'm working with:
stocks
- id
- asset_id
- market_id
- name
- symbol
- IPOYear
- sector
- industry
updates
- id
- [timestamp]
stock_updates
- stock_id
- update_id
I've been using the following query and it was working well for me until I realized it dosen't work if the stock doesn't have an update
select * from stocks s
where #date < (
select top 1 u.timestamp from
updates u,
stock_updates su
where
s.id = su.stock_id and
u.id = su.update_id
order by u.timestamp desc
)
So after some research I came accross outer joins and I think it's what I need to fix my problem I just haven't been able to construct the correct query. The closest I've come is the following, but it returns a record for each time the stock was updated. Thanks in advance for your help!
This is where I'm at now:
select * from stocks s
left outer join stock_updates su on s.id = su.stock_id
left outer join updates u on u.id = su.update_id
where u.[timestamp] < #date
select s.*, u.timestamp
from stocks s
left join
(select su.stock_id, MAX(u.timestamp) timestamp
from updates u
inner join stock_updates su
on u.id = su.update_id
group by su.stock_id
) as u
on s.id = u.stock_id
where u.[timestamp] is null or u.[timestamp] < #date
Something like this perhaps?
SELECT s.*, v.timestamp
FROM stocks s
LEFT JOIN (
SELECT MAX(u.timestamp) AS timestamp, su.stock_id
FROM stock_updates su
INNER JOIN updates u ON (u.id = su.update_id)
GROUP BY su.stock_id
) v ON (v.stock_id = s.stock_id)
Basically it just joins the stocks table to an "inline view" that is the result of a query to determine the maximum timestamp for each stock_id.
I haven't included any filtering by the #date parameter, as your question states
"I'm trying to write a query that returns all the information about a
stock and the last time it was updated"
and for that you don't require any filtering.
This query does exactly that:
select s.*, dr.maxtime,
from stocks s
left join (select MAX(u.timestamp) as maxtime, su.stock_id
from stock_updates su inner join updates u on u.id = su.update_id
group by su.stock_id) dr
on dr.stock_id = s.stock_id
where
maxtime < #date or maxtime is null
[BTW: left join is the same as left outer join]
Try this
select s.*, max(su.timestamp)
from
stocks s
left outer join
stock_update su
on (s.id = su.stock_id)
left outer join
updates u
on (u.id = su.update_id)
group by s.*
It's written off the top of my head. What do you refer to with #date? Does that mean "now"? Do you mean the latest timestamp, or the latest before #date?

retrieve available day from date rante

I think I'm pretty close on this query, but can't seem to crack it, and I'm not sure if I've got the most efficient approach.
I am trying to find a day where a user is not booked from a range of dates where they are booked.
Think staff scheduling. I need to find who is available to work on Tuesday, and is working on other days this week.
My query currently looks like this
SELECT employees.uid, name, date
FROM employees
LEFT JOIN storelocation ON employees.uid = storelocation.uid
LEFT JOIN schedule ON emplyees.uid = schedule.uid
WHERE slid =9308
AND date
BETWEEN '2009-11-10'
AND '2009-12-20'
AND NOT
EXISTS (
SELECT uid
FROM schedule
WHERE date = '2009-11-11'
)
If I don't include the 'Not Exists', I get 1500 results
If I use only the Select form the 'Not Exists', I get 200 results, so both of those queries work independently.
However, my query as I've written it returns 0 results.
You might want to try something more like this:
SELECT employees.uid, name, date
FROM users
LEFT JOIN storelocation ON employees.uid = storelocation.uid
LEFT JOIN schedule ON emplyees.uid = schedule.uid
WHERE slid =9308
AND date BETWEEN '2009-11-10' AND '2009-12-20'
AND employees.uid NOT IN (
SELECT uid
FROM schedule
WHERE date = '2009-11-11'
)
The problem is your NOT EXISTS isn't correllated, and you won't be able to do with this without using table aliases:
SELECT e.uid,
e.name,
s.date
FROM EMPLOYEES e
LEFT JOIN STORELOCATION sl ON sl.uid = e.uid
LEFT JOIN schedule s ON s.uid = e.uid
AND s.date BETWEEN '2009-11-10'AND '2009-12-20'
WHERE slid = 9308
AND NOT EXISTS (SELECT NULL
FROM SCHEDULE t
WHERE t.uid = e.uid
AND t.date = '2009-11-11')
The SELECT in an EXISTS clause doesn't do anything - you could use EXISTS( SELECT 1/0 ..., which should cause a "can not divide by zero" error. But it won't... EXISTS only returns true if 1+ instances match the WHERE/etc clause. There are numerous questions on SO asking about if it matters what's in the SELECT clause if you want to read more.
Jim's answer, typo aside, should be faster in MySQL than the alternative I supplied. To read more about why, check this article: NOT IN vs NOT EXISTS vs LEFT JOIN/IS NULL in MySQL.