I have a hypothetical table "users" with the columns
user_id (auto incremented)
name
foo
bar
last_updated
This table is updated multiple times per day. How can I query to get the last update, per user, per day, going back X days?
Example Data
1 John a b "2013-01-31 02:01:12"
2 Rich c d "2013-01-31 22:41:12"
3 John e f "2013-01-31 22:01:15"
4 Rich g h "2013-02-01 16:01:12"
5 John i j "2013-02-01 22:21:12"
6 Rich k m "2013-02-01 22:21:12"
Desired Return Set:
2 Rich c d 2013-01-31
3 John e f 2013-01-31
5 John i j 2013-02-01
6 Rich k m 2013-02-01
I am able to get the last updated per user overall with the following query, it's applying it to each day that I am struggling with.
SELECT u1.*
FROM users u1
LEFT JOIN users u2
ON (u1.name = u2.name AND u1.user_id < u2.user_id)
WHERE u2.user_id IS NULL
First of all the table name users is very confusing, since these aren't the users but the logins.
Beyond that you're on the right way. You just need to add a comparison on the date in the join.
SELECT u1.*
FROM users u1
LEFT JOIN users u2
ON (u1.name = u2.name AND date_format(u1.last_updated, '%Y-%m-%d') = date_format(u2.last_updated, '%Y-%m-%d') AND u1.user_id < u2.user_id)
WHERE u2.user_id IS NULL
See it work in this SQL fiddle.
Do this by summarizing the data at the date level and then joining back:
select u.*
from users u join
(select u.nanme, DATE(last_updated) as thedate, MAX(last_updated) as maxlastupdated
from users u
group by u.name, DATE(last_updated)
) usum
on u.name = usum.name and
u.last_updated = usum.maxlastupdated
Related
Doing a query on some data but I can't workout how to do this efficiently-- querying users and joining with a table of dates-
I want the users who have do NOT have a record in the second table with a matching date but they have multiple records so if I just did select * from users join dates on users.user_id=dates.user_id where dates.date != '8/3/2020';, shevy and rob would still be returned because row 4 still matches for shevy.
users dates
------- -------
id name id user_id date
1 shevy 1 2 8/1/2020
2 rob 2 1 8/3/2020
3 2 8/10/2020
4 1 8/17/2020
Personally, I would use NOT EXISTS...
SELECT U.ID
,U.NAME
FROM USERS U
WHERE NOT EXISTS (
SELECT 1
FROM DATES D
WHERE D.USER_ID = U.ID
AND D.DATE = '8/3/2020'
)
;
As an aside, this seems like a basic ANSI SQL question and is not really a Snowflake question.
I think you want:
select u.*
from users u left join
dates d
on u.user_id = d.user_id and d.date = '8/3/2020' -- should be 2020-08-03
where d.date is null;
This gets you users that do not have the specified date.
I'm having an issue with a query of mine and how it's being joined. I need to pull some data from multiple tables in regards to CSR agents and the number of dealers they are associated with.
As shown below, I need to return a number of daily contact records for each user as well as a number of dealers associated with that number. Eventually I need to use a formula made from these 1 values, but I can do that with no problem I'm just having an issue getting the two values appropriately.
Currently, I'm getting the same number for both count values, where they should be different.
The code:
SELECT
c.user AS UserID,
COUNT(*) AS NumberOfDailyContacts, -- number of records in contact_events for this user
COUNT(d.csr) AS NumberOfDealerContacts, -- number of dealers associated with this user
FROM contact_events c
JOIN users u
ON c.user = u.id
JOIN dealers d
ON c.dealer_num = d.dealer_num
LEFT JOIN attr_list al
ON d.csr = al.data
GROUP BY UserID;
The fiddle: http://sqlfiddle.com/#!9/bd375/1
Desired output:
12345 | 2 | 3
23456 | 2 | 6
34567 | 2 | 2
45678 | 2 | 2
56789 | 2 | 5
67890 | 2 | 2
78911 | 2 | 4
But currently the fiddle is giving me all 2's for both columns.
The table structure for these tables sucks but it's what I'm given currently. The problem is that the contact events table uses the user ID for the CSR, where the dealer table associates by the 'data' value on the attribute_list table. So I basically have to say:
If the user ID In the contact_events table matches the user_id for a given data field in attr_list, show dealers associated with that user.
Hopefully the fiddle makes this a little more clear but I'll answer any questions you may have.
Use a subquery that joins attr_list with dealers to get the number of dealers per user.
select
c.user as UserID,
count(*) as NumberOfDailyContacts,
al.NumberOfDealerContacts
From contact_events c
join users u
on c.user = u.id
join dealers d
on c.dealer_num = d.dealer_num
left join (
SELECT user_id, COUNT(*) AS NumberOfDealerContacts
FROM attr_list AS al
JOIN dealers AS d ON d.csr = al.data
GROUP BY user_id) AS al
ON al.user_id = c.user
GROUP BY UserID
fiddle
Your joins were out of order, which caused your counts to get messed up. Here's what it should be, no subqueries needed:
SELECT
u.id AS UserID
,COUNT(DISTINCT c.id) AS NumberOfDailyContacts
,COUNT(DISTINCT d.dealer_num) AS NumberOfDealerContacts
FROM users u
LEFT JOIN attr_list al ON u.id = al.user_id
LEFT JOIN dealers d ON d.csr = al.data
LEFT JOIN contact_events c ON c.user = u.id
GROUP BY u.id;
I have a user table in the database where all users of the system are stored.
The table has a user_id and a business_name and a first_name.
Some users are merchants and get a business name,
some users are consumers and get a first name.
In a second table I have transactions with a user_id and a merchant_id (which are defining the transaction) and an amount. Both ids reference to user table.
Table users:
user_id bus_name first_name role_id
1 Thomas 10
2 comp1 7
3 Peter 10
4 comp2 7
(role_id is defining with 10=consumer, 7=merchant)
Table transactions:
trans_id amount user_id merchant_id
1 12 1 2
2 23 3 2
3 34 3 4
4 19 1 4
Now I want to have a query with a result as one table:
This table should contain the transaction with amount, user_id, first_name, merchant_id and bus_name.
I want to get this result:
trans_id amount user_id first_name merchant_id bus_name
1 12 1 Thomas 2 comp1
2 23 3 Peter 2 comp1
3 34 3 Peter 4 comp2
4 19 1 Thomas 4 comp2
I have the problem that either I get only the first_name and empty bus_name or I get only the bus_name but empty first_name.
I am using a left join:
...
left join `users`
on(
(`transactions`.`user_id` = `users`.`user_id`)
)
...
But for this I would get for user_id=1 the first_name=Thomas and the bus_name='' would be empty because I only reference to one line in table and not also to different user with user_id=2.
But I want to say something like:
for trans_id=1
get first_name FROM users WHERE transactions.user_id = users.user_id
AND
get bus_name FROM users WHERE transactions.merchant_id = users.user_id
Thanks for your help, I tried so many things but it does not work.
You have to join the user table twice:
SELECT t.*, u.first_name, m.bus_name
FROM transactions t
JOIN users as u
ON t.user_id = u.user_id
JOIN users as m
ON t.merchant_id = m.merchant_id
you could use a duoble join in users table
select a.trans_id, a.amount , a.user_id, b.first_name, a.merchant_id, c. bus_name
from transactions a
inner join users b on a.user_id = b.user_id and b.role_id = 10
inner join users c on a.merchant_id = c.user_id and c.role_id = 7
To join the user table twice worked fine. With "left join users as consumer" I create a kind of a virtual users table called "consumer", this one is joined. Of course in select I had to adjust table name as well. Same for second "virtual" table od users, called "merchant".
select
`transactions`.`trans_id` AS `trans_id`,
`transactions`.`merchant_id` AS `merchant_id`,
`merchant`.`bus_name` AS `bus_name`,
`transactions`.`user_id` AS `user_id`,
`consumer`.`first_name` AS `first_name`,
`cards`.`card_id` AS `card_id`,
`cards`.`serial_no` AS `serial_no`
from (
`transactions`
left join `cards`
on(
(`cards`.`card_id` = `transactions`.`card_id`)
)
left join `users` as consumer
on(
(`consumer`.`user_id` = `transactions`.`user_id`)
)
left join `users` as merchant
on(
(`merchant`.`user_id` = `transactions`.`merchant_id`)
)
)
mysql SELECT query with left join is not producing the result I am expecting.
I hope someone can show me or point me to the right direction,
I am trying to build a query where I get all the users name from the "users" table and
fetch the sum of all the time they spent for a particular date from the master table. I've used the left join but I am not getting the result as expected.
SUM(m.time_spent) as sum_total_time
FROM master as m
LEFT OUTER JOIN users as u ON u.user_id = m.user_id
WHERE m.date_created >= '2016-05-09'
AND m.date_created <= '2016-05-13'
GROUP BY name
ORDER BY name
master table
master_id user_id time_spent date_created
1 1 40 2016-05-01
2 2 36 2016-05-02
3 3 56 2016-05-03
4 2 33 2016-05-03
5 1 32 2016-05-05
nth nth nth number nth date
users table
user_id first_name last_name
1 James Green
2 Robert Cox
3 Andy Roger
etc etc etc
I want the output result should look like this:
user_id Name sum_total_time
1 James Green 62
2 Robert Cox 69
3 Andy Roger 56
4 Brian Harper 0
5 Angel Lee 0
6 Andrew Martin 55
.....
.....
Nth Name Nth value
You have to select data directly from the master table, group by user and calculate the sum. Then you can join this result with the user table to get all the information about the user.
Could be date conversione issue ..
SUM(m.time_spent) as sum_total_time
FROM master as m
LEFT OUTER JOIN users as u ON u.user_id = m.user_id
WHERE m.date_created >=STR_TO_DATE( '2016-05-09', '%Y-%m-%d)
and/or you have also incomplete sql
SUM(m.time_spent) as sum_total_time
FROM master as m
LEFT OUTER JOIN users as u ON u.user_id = m.user_id
WHERE m.date_created >= '2016-05-09'
AND m.date_created // this condition don't match with nothing..
// could be you forgot a part
Update 1
If you want user totale then
select u.id, u.name, SUM(m.time_spent) as sum_total_time
FROM master as m
Inner JOIN users as u ON u.user_id = m.user_id
WHERE m.date_created >=STR_TO_DATE( '2016-05-09', '%Y-%m-%d)
AND m.date_created <= =STR_TO_DATE('2016-05-13'', '%Y-%m-%d)
Group by u.id
I will solely explain what's related to the question and omit any other unrelated details.
Current Situation:
I have two tables, coin and users.
coin has three fields id, uid, fid.
Coin table relates between users (who already registered in the system and are able to invite friends) and their friends (who already has accepted the invitation and also became members.) -the table only stores successful registrations after being invited-
id is unique index.
uid is to store users id.
fid is to store friends id (the friend who accepted the invitation and became a member of the system).
users has the usual info about users such as id, fname, lname, email ...etc and date_create
Objective:
To find the winners who had made most invitations. In other words.
To find top user(s) who have made the greatest number of invitation and their invited friends must have registered before 2012-08-31. Date is stored in table 'users' column date_create. Date Format is yyyy-mm-dd
Example-
Table coin
id uid fid
1 333 777
2 444 888
3 555 999
4 333 123
5 444 456
6 333 789
Table users
id date_create
333 2012-07-15
444 2012-07-20
555 2012-07-25
777 2012-07-25
888 2012-07-25
999 2012-10-02 <-- I don't need this to be counted
123 2012-07-25
456 2012-07-25
789 2012-07-25
means user 333 has the most number of invitation (invited 3 users) -> 777,123 and 789
and user 444 invited 2 users and user 555 only invited 1 user, but for 555 it will not be counted since his friend (999) registered after 2012-08-31
What i want is user
333 has made 3 invitations before 2012-08-31.
444 has made 2 invitations before 2012-08-31.
What i did so far is: [TOTALLY NOT SURE ABOUT THIS]
SELECT
c.uid,
u.fname,
u.lname,
u.phone,
u.email,
u.country,
COUNT(c.fid) AS NoOfFriends
FROM coin AS c
JOIN users AS u
ON c.uid = u.id
GROUP BY c.uid
ORDER BY NoOfFriends desc
This query brings (as far as I know) the user with most invitations regardless of when his/her friends have registered.
So my question is:
Q1) How to apply the date condition into my query?
I want the user who has the greatest number of friends invitation. His/her invited friends must have accepted the invitation and registered before 2012-08-31. Any other accepted invitations after that date should not be counted.
Please provide me with code examples.
A lot of the answers confused the actual user with the invited friend.
SELECT
c.uid
info.fname,
info.lname,
info.phone,
info.email,
info.country,
info.date_create,
COUNT(c.fid) AS [NoOfFriends]
FROM coin AS c
JOIN users AS friend
ON c.fid = friend.id
LEFT JOIN users AS info
ON c.uid = info.id
WHERE friend.date_create < '20121031'
GROUP BY c.uid
EDIT Fixed the data format
You can use SUM in combination with CASE:
SUM(CASE WHEN <cond> THEN 1 ELSE 0 END) as NoOfFriends
The complete statement would look something like this:
SELECT
c.uid,
u.fname,
u.lname,
u.phone,
u.email,
u.country,
u.`date_create`,
SUM(CASE WHEN f.date_create < 2012-10-31 THEN 1 ELSE 0 END) AS NoOfFriends
FROM coin AS c
JOIN users AS u
ON c.uid = u.id
join users as f
on c.fid = f.id
GROUP BY c.uid, u.fname, u.lname, u.phone, u.email, u.country, u.`date_create`
ORDER BY NoOfFriends desc
The count(expression) function counts the rows where the result of that expression is not null. If the expression just returns true or false all rows will be counted because both true and false are not null.
To use an expression with count you must make it return null if false:
COUNT(friend.date_create < '2012-10-31' or null) AS NoOfFriends
Your method of only grouping by c.uid will work in MySQL, but you will become unstuck with other RDBMS.
In this situation I would move the count to a subquery, in my opinion it is easier to read, and it nicely separates out the query inot the logic you are looking to apply, i.e. Get all invites where the friend registered before a certain date, then joins back to the user that invited them.
SELECT u.ID,
u.fname,
u.lname,
u.phone,
u.email,
u.country,
u.`date_create`,
COALESCE(FriendCount, 0) AS FriendCount
COALESCE(FriendCount2, 0) AS FriendCount2
FROM users AS u
LEFT JOIN
( SELECT coin.UID,
COUNT(coind.fid) AS FriendCount,
COUNT(CASE WHEN Users.Date_Create < '20121031' THEN coind.fid END) AS FriendCount2
FROM coin
INNER JOIN Users
ON coin.FID = users.ID
GROUP BY coin.UID
) c
ON c.uid = u.id
ORDER BY FriendCount2 DESC
If you do not care about total friends, and only those before 31st October then you can simplify this to
SELECT u.ID,
u.fname,
u.lname,
u.phone,
u.email,
u.country,
u.`date_create`,
COALESCE(FriendCount, 0) AS FriendCount
FROM users AS u
LEFT JOIN
( SELECT coin.UID,
COUNT(coind.fid) AS FriendCount
FROM coin
INNER JOIN Users
ON coin.FID = users.ID
WHERE Users.Date_Create < '20121031'
GROUP BY coin.UID
) c
ON c.uid = u.id
ORDER BY FriendCount DESC
This is my answer, thanks for everyone who tried and helped, especially to LastCoder who provided an actual solution rapidly.
SELECT
c.uid,
info.id,
info.fname,
info.lname,
info.phone,
info.email,
info.country,
COUNT(DISTINCT c.fid) AS NoOfFriends -- modified this to get unique invitations
FROM coin c
JOIN users AS friends
ON c.fid = friends.id
LEFT JOIN users AS info
ON c.uid = info.id
WHERE friends.date_create < '20120831'
GROUP BY c.uid
ORDER BY NoOfFriends DESC
LIMIT 10
Honesty, i did not use other solutions because i don't understand SUM(CASE WHEN ...) clearly.