SQL, query to check and list distinct entries that occur in another table within a specific time frame - sql

I'm using Oracle.
I have two tables. One contains users and the other is an access log of sorts. I need to list all users whose latest log entry appears in the log within a specified time frame including the timestamp of the latest entry. A single user can have several entries in the log.
Here are simplified versions of the tables:
Users
|----------------------------------|
| userid| username | name |
|----------------------------------|
| 1 | josm | John Smith |
| 2 | lajo | Laura Jones |
| 3 | miwi | Mike Williams |
| 4 | subo | Susan Brown |
| 5 | peda | Peter Davis |
| 6 | jami | Jane Miller |
|----------------------------------|
Log
|----------------------------------|
| userid| action | timestamp |
|----------------------------------|
| 3 | a | 20-01-2020 |
| 2 | v | 19-11-2019 |
| 2 | y | 02-11-2019 |
| 4 | b | 15-09-2019 |
| 1 | a | 23-05-2019 |
| 6 | y | 22-05-2019 |
| 3 | b | 16-04-2019 |
| 2 | a | 07-01-2019 |
| 5 | v | 18-11-2018 |
| 6 | a | 12-09-2018 |
|----------------------------------|
Desired result if the time frame is set to last six months:
|---------------------------------------|
| username | name | timestamp |
|--------------------------|------------|
| miwi | Mike Williams | 20-01-2020 |
| lajo | Laura Jones | 19-11-2019 |
| subo | Susan Brown | 15-09-2019 |
|---------------------------------------|
Any help will be greatly appreciated.

You can use aggregation:
select u.username, u.userid, max(l.timestamp)
from logs l join
users u
on l.userid = u.userid
group by u.username, u.userid
having max(l.timestamp) >= add_months(sysdate, -6)

Related

Updating table based on the results of previous query

How can I update the table based on the results of the previous query?
The original query (big thanks to GMB) can find any items in address (users table) that have a match in address (address_effect table).
From the result of this query, I want to find the count of address in the address_effect table and add it into a new column in the table “users”. For example, john doe has a match with idaho and usa in the address column so it’ll show a count of ‘2’ in the count column.
Fyi, I'm testing this on my local system with XAMPP (using MariaDB).
user table
+--------+-------------+---------------+--------------------------+--------+
| ID | firstname | lastname | address | count |
| | | | | |
+--------------------------------------------------------------------------+
| 1 | john | doe |james street, idaho, usa | |
| | | | | |
+--------------------------------------------------------------------------+
| 2 | cindy | smith |rollingwood av,lyn, canada| |
| | | | | |
+--------------------------------------------------------------------------+
| 3 | rita | chatsworth |arajo ct, alameda, cali | |
| | | | | |
+--------------------------------------------------------------------------+
| 4 | randy | plies |smith spring, lima, peru | |
| | | | | |
+--------------------------------------------------------------------------+
| 5 | Matt | gwalio |park lane, atlanta, usa | |
| | | | | |
+--------------------------------------------------------------------------+
address_effect table
+---------+----------------+
|address |effect |
+---------+----------------+
|idaho |potato, tater |
+--------------------------+
|canada |cold, tundra |
+--------------------------+
|fremont | crowded |
+--------------------------+
|peru |alpaca |
+--------------------------+
|atlanta |peach, cnn |
+--------------------------+
|usa |big, hard |
+--------+-----------------+
Use a correlated subquery which returns the number of matches:
UPDATE user u
SET u.count = (
SELECT COUNT(*)
FROM address_effect a
WHERE FIND_IN_SET(a.address, REPLACE(u.address, ', ', ','))
)
See the demo.
Results:
> ID | firstname | lastname | address | count
> -: | :-------- | :--------- | :------------------------- | ----:
> 1 | john | doe | james street, idaho, usa | 2
> 2 | cindy | smith | rollingwood av,lyn, canada | 1
> 3 | rita | chatsworth | arajo ct, alameda, cali | 0
> 4 | randy | plies | smith spring, lima, peru | 1
> 5 | Matt | gwalio | park lane, atlanta, usa | 2
Notice: I checked it in MySQL, but not in MariaDB.
The count column of users table may be able to be updated using UPDATE statement with INNER JOIN. Then you can use a query that modifies the original query to use "GROUP BY".
UPDATE users AS u
INNER JOIN
(
-- your original query modified
SELECT u.ID AS ID, count(u.ID) AS count
FROM users u
INNER JOIN address_effect a
ON FIND_IN_SET(a.address, REPLACE(u.address, ', ', ','))
GROUP BY u.ID
) AS c ON u.ID=c.ID
SET u.count=c.count;

Create duplicate records based on values in another table (SQL Server)

Currently I am working on a piece of requirement where I need to create a copy or duplicate a record of a table based on values in another table based on priority. Below is my source data
PrivilegeTbl
+----------+-----------+
| Priority | UserLevel |
+----------+-----------+
| 1 | Admin |
| 2 | SuperUser |
| 3 | User |
| 4 | Guest |
+----------+-----------+
UserTbl
+----------+-----------+-----------+
| UserName | UserLevel | Dept |
+----------+-----------+-----------+
| Alex | User | IT |
| George | Guest | Marketing |
| Bob | Admin | HR |
+----------+-----------+-----------+
AccessTbl
+----------+-----------+-----------------+
| UserName | UserLevel | AccessGrantedOn |
+----------+-----------+-----------------+
| Alex | User | 01-Jan-18 |
| Alex | Admin | 01-Jan-19 |
| George | Guest | 01-Jan-20 |
| Bob | SuperUser | 01-Jan-17 |
| Bob | Admin | 01-Jan-18 |
+----------+-----------+-----------------+
Expected Output:
+----------+-----------+-----------+
| UserName | UserLevel | Dept |
+----------+-----------+-----------+
| Alex | Admin | IT |
| Alex | User | IT |
| George | Guest | Marketing |
| Bob | Admin | HR |
+----------+-----------+-----------+
Here I need to check if a User in UserTbl having additional UserLevel in AccessTbl which is in higher priority as per PrivilegeTbl and create a copy. So in the expected output Alex is having two records. Bob in UserTbl having additional UserLevel in AccessTbl but the priority is lower so expected output contains only one record as is.
SELECT a.UserName, CASE WHEN ap.Priority > up.Priority THEN up.UserLevel ELSE ap.UserLevel END AS UserLevel, u.Dept
FROM AccessTbl a JOIN UserTbl u ON (a.UserName = u.UserName)
JOIN PrivilegeTbl up ON (u.UserLevel = up.UserLevel)
JOIN PrivilegeTbl ap ON (a.UserLevel = ap.UserLevel)
WHERE up.Priority >= ap.Priority

What SQL query should I perform to get the result set as I expected?

I'm having problem to get the result I need :/ these are my tablets. On Postgresql
table: logins table: users
+------------------------+ +------------------+
| iduser | date | | iduser | name |
|------------------------| |------------------|
| 1 |'2017-06-06'| | 1 | Joe |
|------------------------| |------------------|
| 1 |'2017-06-06'| | 2 | Jane |
|------------------------| |------------------|
| 2 |'2017-06-07'| | 3 | Mary |
|------------------------| +------------------+
| 3 |'2017-06-07'|
|------------------------|
| 3 |'2017-06-07'|
|------------------------|
| 3 |'2017-06-07'|
+------------------------+
Im Using this query:
SELECT name, date, count(*) FROM logins l
LEFT JOIN users u
ON u.iduser= l.iduser
GROUP BY
u.name,l.date
ORDER BY
l.date
This it what I got:
+-----------------------------------+
| name | date | count |
|-----------------------------------|
| Joe | '2017-06-06' | 2 |
|-----------------------------------|
| Jane | '2017-06-07' | 1 |
|-----------------------------------|
| Mary | '2017-06-07' | 3 |
+-----------------------------------+
but what I really need to get from the result its this:
+-----------------------------------+
| name | date | count |
|-----------------------------------|
| Joe | '2017-06-06' | 2 |
|-----------------------------------|
| Jane | '2017-06-06' | 0 |
|-----------------------------------|
| Mary | '2017-06-06' | 0 |
|-----------------------------------|
| Joe | '2017-06-07' | 0 |
|-----------------------------------|
| Jane | '2017-06-07' | 1 |
|-----------------------------------|
| Mary | '2017-06-07' | 3 |
+-----------------------------------+
What should I do? please help!!! thanks a lot! ^^
In SQL Server & Postgres:
Getting all combinations of date and users, then left join to login:
select
d.date
, u.name
, count(l.iduser) as login_count
from (select distinct date from logins) d
cross join users u
left join logins l
on l.iduser=u.iduser
and l.date=d.date
group by d.date, u.name
rextester demo (sql server): http://rextester.com/THJE85313
rextester demo (postgres): http://rextester.com/BNHE97192
returns:
+---------------------+------+-------------+
| date | name | login_count |
+---------------------+------+-------------+
| 2017-06-06 00:00:00 | Jane | 0 |
| 2017-06-07 00:00:00 | Jane | 1 |
| 2017-06-06 00:00:00 | Joe | 2 |
| 2017-06-07 00:00:00 | Joe | 0 |
| 2017-06-06 00:00:00 | Mary | 0 |
| 2017-06-07 00:00:00 | Mary | 3 |
+---------------------+------+-------------+

SQL only select rows with max date within each user

SQL beginner here. I've got a simple test that users take, and each row is the answer to one of their questions. They're allowed to take the exam once per day, so some people take it a second time on another day, and thus will have many rows with different test dates. What I'm basically trying to do is get each user's most recent score.
Here is what my data looks like (table name is dumdum):
+----------+----------------+----------+------------------+
| USERNAME | CORRECT_ANSWER | RESPONSE | DATE_TAKEN |
+----------+----------------+----------+------------------+
| matt | 1 | 1 | 3/23/15 1:04:26 |
| matt | 2 | 2 | 3/23/15 1:04:28 |
| matt | 3 | 3 | 3/23/15 1:04:23 |
| david | 1 | 3 | 3/20/15 1:04:25 |
| david | 2 | 2 | 3/20/15 1:04:28 |
| david | 3 | 1 | 3/20/15 1:04:30 |
| david | 1 | 1 | 3/21/15 11:03:14 |
| david | 2 | 3 | 3/21/15 11:03:17 |
| david | 3 | 2 | 3/21/15 11:03:19 |
| chris | 1 | 2 | 3/17/15 12:45:52 |
| chris | 2 | 2 | 3/17/15 12:45:56 |
| chris | 3 | 3 | 3/17/15 12:45:59 |
| peter | 1 | 1 | 3/19/15 2:45:33 |
| peter | 2 | 3 | 3/19/15 2:45:35 |
| peter | 3 | 2 | 3/19/15 2:45:38 |
| peter | 1 | 1 | 3/20/15 12:32:04 |
| peter | 2 | 2 | 3/20/15 12:32:05 |
| peter | 3 | 3 | 3/20/15 12:32:05 |
+----------+----------------+----------+------------------+
and what I'm trying to get in the end...
+----------+------------------+-------+
| USERNAME | MOST_RECENT_TEST | SCORE |
+----------+------------------+-------+
| matt | 3/23/2015 | 100 |
| david | 3/21/2015 | 33 |
| chris | 3/17/2015 | 67 |
| peter | 3/20/2015 | 100 |
+----------+------------------+-------+
I ran into some trouble because I need to go by day, and not by day/time, so I had to do a weird maneuver where I went to character and back to date... This is what I have so far, but I can't figure out how to use only the scores from the most recent test (right now it's factoring in all scores from every test ever taken)...
SELECT username, to_date(substr(max(test_date),1,9),'dd-MON-yy') as most_recent_test, round((sum(case when response=correct_answer then 1 end)/3)*100,0) as score
FROM dumdum group by username
Any help would be appreciated! Thanks!
There are several solutions to this problem this one uses the WITH clause and the RANK function.
It also uses the TRUNC function rather than to_date(substr(
with mxDate as
(SELECT USERNAME,
TRUNC(DATE_TAKEN) as MOST_RECENT_TEST,
CASE WHEN CORRECT_ANSWER = RESPONSE THEN 1 else 0 END as SCORE,
RANK () OVER (PARTITION BY USERNAME
ORDER BY TRUNC(DATE_TAKEN) DESC) Rk
FROM dumdum)
SELECT
USERNAME,
MOST_RECENT_TEST,
SUM(SCORE)/3 * 100
FROM
mxDate
WHERE
rk = 1
GROUP BY
USERNAME,
MOST_RECENT_TEST
Demo

SQL Query - Grouping Data

So every morning at work we have a stand-up meeting. We throw the nearest object to hand around the room as a method of deciding who speaks in what order. Being slightly odd I decided it could be fun to get some data on these throws. So, every morning I memorise the order of throws (as well as other relevant things like who dropped the ball/strange sponge object that was probably once a ball too and who threw to someone who'd already been or just gave an atrocious throw), and record this data in a table:
+---------+-----+------------+----------+---------+----------+--------+--------------+
| throwid | day | date | thrownum | thrower | receiver | caught | correctthrow |
+---------+-----+------------+----------+---------+----------+--------+--------------+
| 1 | 1 | 10/01/2012 | 1 | dan | steve | 1 | 1 |
| 2 | 1 | 10/01/2012 | 2 | steve | alice | 1 | 1 |
| 3 | 1 | 10/01/2012 | 3 | alice | matt | 1 | 1 |
| 4 | 1 | 10/01/2012 | 4 | matt | justin | 1 | 1 |
| 5 | 1 | 10/01/2012 | 5 | justin | arif | 1 | 1 |
| 6 | 1 | 10/01/2012 | 6 | arif | pete | 1 | 1 |
| 7 | 1 | 10/01/2012 | 7 | pete | greg | 0 | 1 |
| 8 | 1 | 10/01/2012 | 8 | greg | alan | 1 | 1 |
| 9 | 1 | 10/01/2012 | 9 | alan | david | 1 | 1 |
| 10 | 1 | 10/01/2012 | 10 | david | dan | 1 | 1 |
| 11 | 2 | 11/01/2012 | 1 | dan | david | 1 | 1 |
| 12 | 2 | 11/01/2012 | 2 | david | alice | 1 | 1 |
| 13 | 2 | 11/01/2012 | 3 | alice | steve | 1 | 1 |
| 14 | 2 | 11/01/2012 | 4 | steve | arif | 1 | 1 |
| 15 | 2 | 11/01/2012 | 5 | arif | pete | 0 | 1 |
| 16 | 2 | 11/01/2012 | 6 | pete | justin | 1 | 1 |
| 17 | 2 | 11/01/2012 | 7 | justin | alan | 1 | 1 |
| 18 | 2 | 11/01/2012 | 8 | alan | dan | 1 | 1 |
| 19 | 2 | 11/01/2012 | 9 | dan | greg | 1 | 1 |
+---------+-----+------------+----------+---------+----------+--------+--------------+
I've now got quite a few days worth of data for this, and I'm starting to run some queries on it for my own purposes (I've not told the rest of the team yet...wouldn't like to influence the results). I've done a few with no issues, but I'm stuck trying to get a certain result out.
What I'm looking for is the number of times each person has been the last team member to receive the ball. Now, as you can see on the table, due to absences etc the number of throws per day is not always constant, so I can't simply select the receiver by thrownum.
In the case for the data above, it would return:
+--------+-------------------+
| person | LastReceiverTotal |
+--------+-------------------+
| dan | 1 |
| greg | 1 |
+--------+-------------------+
I've got this far:
SELECT MAX(thrownum) AS LastThrowNum, day FROM Throws GROUP BY day
Now, this returns some useful data. I get the highest thrownum for each and every day. It would seem like all I need to do is get the receiver for this value, and then get a count grouped by receiver to get my answer. This doesn't work, though, because the resultset isn't what it seems due to the above query using aggregate functions.
I suspect there's a much better way of designing tables to store the data to be honest, but equally I'm also sure there's a way to get this information with the tables as they are - some kind of inner query? I can't figure out how it would work. Can anyone shed some light on how this would be done?
The query that you have gives you the biggest thrownum for each day.
With that, you just do a inner join with your table and get the receiver and the number of times he happears.
select t.receiver as person, count(t.day) as LastReceiverTotal from Throws t
inner join (SELECT MAX(thrownum) AS LastThrowNum, day FROM Throws GROUP BY day) a on a.LastThrowNum = t.thrownum and a.day = t.day
group by t.receiver