Active Record Rails query - mysql

Could someone please help me on Rails query regarding this
I have mysql table like this. There is a recreation club. ticket_id is unique. There is entry and exit for each ticket (member).
I want to identify on particular time, how many members are inside. suppose if i want to know the inside members by the time '2015-12-17 16:48:00'.
id ticket_id event_type timestamp
1739 869 entry 2015-12-17 15:09:51
1740 869 entry 2015-12-17 17:46:55
1730 864 exit 2015-12-17 16:48:27

Assumption 1 : Every ticket_id can have multiple entries and exits. The data you have provide has 2 entries for ticket_id 869. Hence, the ActiveQuery results are subject to uniq. Even when a person has multiple entries (or exits) before required time, they are counted only once.
Assumption 2 : In some cases, between 2 entries, an exit may not be captured, as demonstrated by the sample data. Some discrepancy in the result might creep in because of this.
You can use the following query to obtain all people who are inside at one particular point of time, say reqd_timestamp :
#entered = Member.where("timestamp <= ? AND event_type = ?",
reqd_timestamp, 'entry')
.pluck(:ticket_id).uniq
#exited = Member.where("timestamp <= ? AND event_type = ?",
reqd_timestamp, 'exit')
.pluck(:ticket_id).uniq
#inside = (#entered - #exit).count
Please note, ticket_id 869 will be counted as being inside as of 2015-12-17 16:48:00 because he has an entry with id 1739 prior to the timestamp. He actually may have exited before stipulated time and subsequently making another entry as id 1740. But no data being available, that particular information cannot be captured.

Try this
#members = Member.where("date(timestamp) =? AND event_type=?", '2015-12-17 16:48:00', 'entry').count

Related

Previous record in mysql with duplicated records and random ID

My applicants table has multiple records with the same date and an index with missing and non-sequential values, much like this:
ID date Name
3422 2021-02-08 Priscilla
3421 2021-02-04 Rahul
3437 2021-02-04 Ella
3435 2021-02-04 Aarushi
3436 2020-02-03 Colin
I need to get the previous record to the current one in database order by date. So if the current record is Ella's I need to return Rahul's and then Priscilla's.
I've dealt with the next equivalent by using an IN statement to exclude IDs in multiple dates:
SELECT *
FROM applicants
WHERE date_created <= $mdata['currdate']
AND applicant_id NOT IN ( . $mdata['currids'] . )
ORDER
BY date_created DESC
LIMIT 1
But this doesn't work with previous calls.
I've seen several previous answers but none quite fit my situation and I'm not versed enough in SQL to modify them to my needs. Some pointers towards to a solution will be very welcome.

SQL - Select Closest Preceding Date

I have a database table that contains one or more entries for each patient. These contain free text and additional information about a test request. Querying on a patient would for example return:-
TestID PatientID RequestMade FreeText
1 23 13/12/2015 11:00:00 Feeling breathless
1125 23 07/04/2016 09:31:15 Unexplained fractures
2556 23 04/12/2016 16:20:21 Check liver function – on statins
When viewing test results I have to pull up the request information relating to the test which will be the last one prior to the test. The results have a TestDate so a TestDate of '13/04/2016 14:21:30' should display the request of '07/04/2016 09:31:15'. I am unsure how to code this efficiently as returning every entry for a patient and doing a date comparison on each one seems not the best way to tackle it.
If you want the one test before another test for a single patient and the test you are looking for only appears once, then you can do this with a single query as:
select t.*
from tests t
where t.patientid = 23 and
t.requestmade < (select t2.requestmade
from tests t2
where t2.patientid = t.patientid and
t2.testid = ?
)
order by t.requestmade desc
limit 1;

MySQL get only the row with the highest ID of a query

Hello Stackoverflow community,
This is my first post here, so I apologize for my bad English! :)
If you don't want to read everything, the Questions are marked like this.
The title is a little misleading, but I didn't know how to explain it in a better way, but the detailed explanation should let you understand:
I have a big log table (about 500000 rows at this time), where a game server logs many actions that occur in the game. I want to extract some specific log rows the most efficient way.
I can't change the logging system of the game server, if I could I would change it to many more log tables, to create more compact logs. (because executing queries on that table takes it's time..)
Now my problem is, that I want to get the last log row of a specific type and from a specific player id to get the players last action, and I don't know how to do that in an efficient way.
Example:
SELECT * FROM log WHERE logType = "PLAYER" AND playerID = [playerID] ORDER BY time DESC LIMIT 1
Then the output on the website would be:
You last action was [the human readable action and additional information].
MySQL profiling now tells me that sorting of the results takes the most amount of time.
Now my question is: Is it possible to get only the last row of a specific player id and type?
I guess it could be done with the ID, cause it has auto_increment. So is it possible to get the row with the highest ID, a specific type and a specific player id?
The table structure:
ID(int) | logType(varchar) | time(datetime) | playerID(int) | positionX(int) | positionY(int) | actionID(int) | action(varchar) | hints(varchar) | ip(varchar) | itemNumber(int)
Explaination:
ID: the unique id of the logged action
logType: the type of the logged action (Example: "PLAYER" or "ITEM")
time: the time the action occured
playerID: the id of the player (or other id's related to that type)
positionX: X position in the game
positionY: Y position in the game
actionID: an id in relation to the action (Example: If the log action is "KILLED_BY_PLAYER", then the player id of the other player who killed the player)
action: the action that is logged (Example: KILLED_BY_PLAYER)
hints: Some useful hints like the name of the player
ip: The IP of the player
itemNumber: The number of the Item, if an Item is involved, else NULL
Thanks for your help. :)
to get the highest id:
select max(id) from table;
the others you could put a where clause in the select statement.
You should be able to use your query where you sort on time with LIMIT 1. If your sort on the column time is slow, you must make sure the indexes are optimized (run explain on your query). You will need indexes on both logtype, player_id and time. Even with 500 000 rows this shouldn't be a problem...

How can I find days between different paired rows?

I've been racking my brain about how to do this in one query without PHP code.
In a nutshell, I have a table that records email activity. For the sake of this example, here is the data:
recipient_id activity date
1 delivered 2011-08-30
1 open 2011-08-31
2 delivered 2011-08-30
3 delivered 2011-08-24
3 open 2011-08-30
3 open 2011-08-31
The goal: I want to display to users a single number that tells how many recipients open their email within 24 hours.
E.G. "Users that open their email within 24 hours: 13 Readers"
In the case of the sample data, above, the value would be "1". (Recipient one was delivered an email and opened it the next day. Recipient 2 never opened it and recipient 3 waited 5 days.)
Can anyone think of a way to express the goal in a single query?
Reminder: In order to count, the person must have a 'delivered' tag and at least one 'open' tag. Each 'open' tag only counts once per recipient.
** EDIT ** Sorry, I'm using MySQL
Here is a version in mysql.
select count(distinct recipient_id)
from email e1
where e1.activity = 'delivered'
and exists
(select * from email e2
where e1.recipient_id = e2.recipient_id
and e2.activity = 'open'
and datediff(e2.action_date,e1.action_date) <= 1)
The basic principle is that you want to find a delivered row for a recipient that also has an open within 24 hours.
The datediff() is a good way to do the date arithmetic in mysql -- other dbs will vary on exact methods for this step. The rest of the sql will work anywhere.
SQLFiddle here: http://sqlfiddle.com/#!2/c9116/4
Untested, but should work ;) Don't know which SQL dialect you use, so I've used TSQL DATEDIFF function.
select distinct opened.recipient_id -- or count(distinct opened.recipient_id) if you want to know number
from actions as opened
inner join actions as delivered
on opened.recipient_id = delivered.recipient_id and delivered.activity = 'delivered'
where opened.activity = 'open' and DATEDIFF(day, delivered.date, opened.date) <= 1
Edit: I'd confused opened with delivered - now replaced.
Assumptions: MySql, table is called "TABLE"
Ok, I am not 100% on this, because I don't have a copy of the table to run it against, but I think that you could do something like this:
SELECT COUNT(DISTINCT t1.recipient_id) FROM TABLE t1
INNER JOIN TABLE t2 ON t1.recipient_id = t2.recipient_id AND t1.activity != t2.activity
WHERE t1.activity in ('delivered', 'open') AND t2.activity in ('delivered', 'open')
AND ABS(DATEDIFF(t1.date, t2.date)) = 1
Basically, you are joining a table onto itself, where the activities don't match, but recipient_ids do, and the status is either 'delivered' or 'open'. What you would end up getting, is a result that looks like this:
1 delivered 2011-08-30 1 open 2011-08-31
You are then doing a diff between the two dates (with an absolute value, because we don't know which order they will be in) and making sure that it is equal to 1 (or 24 hours).

How can I find the correct prior status row in this table with a SQL query?

Imagine a workflow for data entry. Some forms come in, they are typed into a system, reviewed, and hopefully approved. However, they can be rejected by a manager and will have to be entered again.
So, an ideal workflow would go like this:
recieved > entered > approved
But this COULD happen:
received > entered > rejected > entered > rejected > approved
At each stage, we record who updated the form to its current status - who entered it, who rejected it, or who approved it. So the forms status table looks like this:
form_id status updated_by updated_at
1 received Bob (timestamp)
1 entered Bob (timestamp)
1 approved Susan (timestamp)
2 received Bob (timestamp)
2 entered Bob (timestamp)
2 rejected Susan (timestamp)
2 entered Carla (timestamp)
2 rejected Susan (timestamp)
2 entered Sam (timestamp)
2 approved Susan (timestamp)
Here's what I'm trying to do: write a rejection report. I want a row for each rejection, and joined to that row, I want to see who did the work that got rejected.
As a human, I can see that, for a given status row with status 'rejected', the row that will tell me who did the faulty work will be the one that
shares the same form_id and
has a prior timestamp closest to the rejection.
But I'm having trouble telling MySQL that.
Can anybody see how to construct this query?
A subselect ended up working for me.
SELECT
`s1`.`form_id`,
(
SELECT
`s2`.`updated_by`
FROM
statuses s2
WHERE
`s2`.`form_id` = `s1`.`form_id`
AND
`s2`.`updated_at` < `s1`.`updated_at`
ORDER BY
`s2`.`updated_at` DESC
LIMIT 1
) AS 'made_rejected_change'
FROM
statuses s1
WHERE
`s1`.`status` = 'rejected'
Another solution that uses subselect (this time not a correlated subquery):
SELECT
w1.*,
w2.entered_by
FROM (
SELECT
wr.form_id,
wr.updated_at AS rejected_at,
wr.updated_by AS rejected_by,
MAX(we.updated_at) AS entered at
FROM workflow wr
INNER JOIN workflow we ON we.status = 'entered'
AND wr.form_id = we.form_id
AND wr.updated_at > we.updated_at
WHERE wr.status = 'rejected'
GROUP BY
wr.form_id,
wr.updated_at,
wr.updated_by
) w1
INNER JOIN workflow w2 ON w1.form_id = w2.form_id
AND w1.entered_at = w2.updated_at
The subselect lists all the rejecters and the immediately preceding entered timestamps. Then the table is joined once again to extract the names corresponding to the entered_at timestamps.
You want to get the rejected timestamp and then figure out the entry that appeared right before it based on the timestamp. I'm assuming that timestamp actually holds a date/time and isn't an SQL server timestamp field (completely different).
declare #rejectedTimestamp timestamp
select #rejectedTimestamp = timestamp
from table
where status = 'rejected'
select top 1 *
from table
where timestamp < #rejectedtimestamp
order by timestamp desc