SQL query to find the time difference - sql

I want to find the time difference between the login and logout of a user.
The hard part for me is that both login and logout time is in one column and there is a "Status" column to show if it is login/logout.
Example:
Timestamp Status UserName
2015-04-26 20:12:33 Login Grashia
2015-04-26 23:22:13 Logout Grashia
How do I query this?
I tried the DATEDIFF function but I know thats not the right way.

Suppose you have the following table schema
CREATE TABLE `user_log` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`user_id` int(11) DEFAULT NULL,
`dt` datetime DEFAULT NULL,
`status` varchar(15) COLLATE utf8_bin DEFAULT NULL,
PRIMARY KEY (`id`)
);
with this sort of data
+----+---------+---------------------+--------+
| id | user_id | dt | status |
+----+---------+---------------------+--------+
| 1 | 1 | 2015-09-23 08:35:36 | Login |
| 2 | 1 | 2015-09-23 17:15:44 | Logout |
| 3 | 1 | 2015-09-24 08:55:18 | Login |
| 4 | 2 | 2015-09-23 09:00:16 | Login |
| 5 | 2 | 2015-09-23 18:00:23 | Logout |
+----+---------+---------------------+--------+
You can use this query
SELECT i.user_id, i.dt AS 'login_dt', IFNULL(o.dt, '-') AS 'logout_dt',
TIMEDIFF(IFNULL(o.dt, NOW()), i.dt) AS 'total_time'
FROM
(SELECT * FROM user_log WHERE `status`='Login') i
LEFT OUTER JOIN (SELECT * FROM user_log WHERE `status`='Logout') o
ON i.user_id=o.user_id AND DATE(i.dt)=DATE(o.dt)
to get this result
+---------+---------------------+---------------------+------------+
| user_id | login_dt | logout_dt | total_time |
+---------+---------------------+---------------------+------------+
| 1 | 2015-09-23 08:35:36 | 2015-09-23 17:15:44 | 08:40:08 |
| 1 | 2015-09-24 08:55:18 | - | 00:10:23 |
| 2 | 2015-09-23 09:00:16 | 2015-09-23 18:00:23 | 09:00:07 |
+---------+---------------------+---------------------+------------+
You have to add required indices and set proper table engine for optimum performance

if it is SQL server your query will look like this:
I suppose that there should be a column userid:
You need to join two table to make compare data in the same column
SELECT in. username, DATEDIFF(minute, in.timestamp, out.timestamp)
FROM
(SELECT username, timestamp FROM logtable WHERE status = 'Login') as IN
INNER JOIN
(SELECT username, timestamp FROM logtable WHERE status = 'logout') AS OUT
ON in.userid = out.useriD
you can read more about DATEDIFF function here:
https://msdn.microsoft.com/en-us/library/ms189794.aspx

Related

Response Slow when Order BY is added to my SQL Query

I have the following job_requests table schema as shown here
+-------------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-------------+--------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| available_to. | integer[] | NO | | | |
| available_type | varchar(255) | NO | | NULL | |
| start_at | varchar(255) | NO | | NULL | |
+-------------+--------------+------+-----+---------+----------------+
I have the following query to return a list of records and order them by the type_of_pool value
WITH matching_jobs AS (
SELECT
job_requests_with_distance.*,
CASE WHEN (users.id = ANY (available_to) AND available_type = 0) THEN 'favourite'
ELSE 'normal'
END AS type_of_pool
FROM (
SELECT
job_requests.*,
users.id AS user_id,
FROM
job_requests,
users) AS job_requests_with_distance
LEFT JOIN users ON users.id = user_id
WHERE start_at > NOW() at time zone 'Asia/Kuala_Lumpur'
AND user_id = 491
AND (user_id != ALL(coalesce(unavailable_to, array[]::int[])))
)
SELECT
*
FROM
matching_jobs
WHERE (type_of_pool != 'normal')::BOOLEAN
ORDER BY
array_position (ARRAY['favourite','exclusive','normal']::text[], type_of_pool),
LIMIT 30
If i remove the ORDER BY function, it takes about 3ms but when I add the ORDER BY function, it takes about 1.3seconds to run.
Not sure how do i optimize this query to make it faster? I have read using Indexes and all but not sure how an index will help in this scenario.
Any help is appreciated.

Postgresql row gets hidden after update, can't select it

I'm having this problem where my rows are being "hidden" (or SELECT * doesn't retrieve them) after updating.
I've tried with CLI and with a GUI (DBeaver) but the result it's the same, here is an example:
select * from users limit 4;
id | email | password | status | role | created_at | updated_at
----+--------------------------------+-----------------+----------+--------+----------------------------+----------------------------
8 | Brad.Bailey#gmail.com | qYHsmrKWaiaiZxI | disabled | seller | 2019-09-06 21:43:08.043-03 | 2019-08-13 16:04:25.233-03
9 | Marcelino_Prohaska97#gmail.com | sUMuOM_gXCPxz19 | disabled | seller | 2019-06-14 15:39:45.447-03 | 2019-06-25 12:54:01.023-03
10 | Gino_Blick#gmail.com | iOkZQhc7JSsQcpY | disabled | seller | 2020-02-13 13:39:16.26-03 | 2019-12-18 17:02:37.938-03
11 | Tiffany.Schuster16#yahoo.com | Bw2OhPUtIRcWxZF | active | seller | 2018-07-30 08:01:29.942-03 | 2019-09-03 10:50:40.314-03
(4 rows)
Then
update users set email = 'test#test.com' where id = 8;
UPDATE 1
And then, this happens:
select * from users limit 4;
id | email | password | status | role | created_at | updated_at
----+--------------------------------+-----------------+----------+--------+----------------------------+----------------------------
9 | Marcelino_Prohaska97#gmail.com | sUMuOM_gXCPxz19 | disabled | seller | 2019-06-14 15:39:45.447-03 | 2019-06-25 12:54:01.023-03
10 | Gino_Blick#gmail.com | iOkZQhc7JSsQcpY | disabled | seller | 2020-02-13 13:39:16.26-03 | 2019-12-18 17:02:37.938-03
11 | Tiffany.Schuster16#yahoo.com | Bw2OhPUtIRcWxZF | active | seller | 2018-07-30 08:01:29.942-03 | 2019-09-03 10:50:40.314-03
12 | Brody_Pollich#yahoo.com | ZlFy3kEUSrmxHAa | disabled | seller | 2018-07-06 13:18:29.936-03 | 2019-08-03 21:46:22.296-03
(4 rows)
The thing is, the row still exists, but it is not shown by SELECT *
select * from users where id = 8 limit 10;
id | email | password | status | role | created_at | updated_at
----+---------------+-----------------+----------+--------+----------------------------+----------------------------
8 | test#test.com | qYHsmrKWaiaiZxI | disabled | seller | 2019-09-06 21:43:08.043-03 | 2019-08-13 16:04:25.233-03
(1 row)
Here is the structure of my table (code by Dbeaver) and the version of my Postgresql:
CREATE TABLE public.users (
id serial NOT NULL,
email varchar(255) NOT NULL,
"password" varchar(255) NOT NULL,
status varchar(255) NOT NULL,
"role" varchar(255) NOT NULL,
created_at timestamptz NOT NULL,
updated_at timestamptz NOT NULL,
CONSTRAINT users_email_unique UNIQUE (email),
CONSTRAINT users_pkey PRIMARY KEY (id)
);
SELECT version();
version
--------------------------------------------------------------------------------------------
-------------------------------------
PostgreSQL 12.3 (Ubuntu 12.3-1.pgdg18.04+1) on x86_64-pc-linux-gnu, compiled by gcc (Ubuntu 7.5.0-3ubuntu1~18.04) 7.5.0, 64-bit
(1 row)
Thanks.
Remove limit and apply order by:
select * from users order by id;
Is ID = 8 back?
Documentation says:
Because the order of the rows in the database table is unspecified, when you use the LIMIT clause, you should always use the ORDER BY clause to control the row order. If you don’t do so, you will get a result set whose rows are in an unspecified order.
which means that you saw record with ID = 8 by accident; no guarantee you'll get it again just with the limit clause.

SQL - get from table and join with same table

I have two tables, ChatRoom and ChatRoomMap, I want to get a list of chatrooms a user belongs to, along with all the other users in each chatroom.
// this contains a map of user to chatroom, listing which user is in what room
CREATE TABLE ChatRoomMap
(
user_id bigint NOT NULL,
chatroom_id text NOT NULL,
CONSTRAINT uniq UNIQUE (userid, roomid)
)
// sample values
==========================
| user_id | chatroom_id |
| 1 | 7 |
| 1 | blue |
| 7 | red |
==========================
And
CREATE TABLE ChatRoom
(
id text NOT NULL,
admin bigint,
name text,
created timestamp without time zone NOT NULL DEFAULT now(),
CONSTRAINT uniqid UNIQUE (id)
)
// sample values
======================================================
| id | admin | name | timestamp |
| blue | 7 | blue room | now() |
| red | 2 | red | now() |
| 7 | 11 | mine | now() |
======================================================
To get a list of rooms a user is in, I can do:
SELECT DISTINCT ON (id) id, userid, name, admin
FROM ChatRoomMap, ChatRoom WHERE ChatRoomMap.user_id = $1 AND ChatRoomMap.chatroom_id = ChatRoom.id
This will get me a distinct list of chat rooms a user is in.
I would like to get the distinct list of rooms along with all the users in each room (concatenation of all as a separate column), how can this be done?
Example result:
=======================================================
| user_id | chatroom_id | name | admin | other_users |
| 10 | 7 | One | 1 | 1, 2, 3, 8 |
| 10 | 4 | AAA | 10 | 7, 11, 15 |
=======================================================
First up, use proper joins - the explicit join syntax was introduced to the SQL92 standard and the major vendors implemented it in the early 2000's (and it's the only way to achieve an outer join).
Try this:
SELECT DISTINCT id, crm2.user_id, name, admin,
FROM ChatRoomMap crm1
JOIN ChatRoom ON crm1.chatroom_id = ChatRoom.id
LEFT JOIN ChatRoomMap crm2 ON crm2.chatroom_id = crm1.chatroom_id
AND crm2.user_id != crm1.user_id -- only other users
WHERE crm1.user_id = $1
The LEFT JOIN is needed in case there are no other users in the room it will still list the room (with a null for other user id).

SQL design for notification of new registered users

I'm with a great difficulty in formulate a SQL for a module of notifications when a new user register.
I have a database of Notifications, I set up a notification to be sent. Examples:
Send notification when a man and blue eyes register;
Send notification when a woman register;
Send a notification when a blue-eyed woman, brown and work in the company Foo;
With these rules we can see that there can be several possibilities (so the table columns are optional).
Some details:
The table columns are defined as integers because are FK. I just did not put tables because the structure is unnecessary, since the SQL will only relashionship between User and Notification;
The date field is used to store both the date of registration of the notice of such person. So I can only filter to notify the new register of user;
Table Structure
User:
+------------+----------+------+-----+---------+------------+
| Field | Type | Null | Key | Default | Extra |
+------------+----------+------+-----+---------+------------+
| Id | int(11) | NO | PRI | | auto_incre |
| Gender | int(11) | YES | | | |
| HairColor | int(11) | YES | | | |
| EyeColor | int(11) | YES | | | |
| Company | int(11) | YES | | | |
| Date | datetime | NO | | | |
| ... | | | | | |
+------------+----------+------+-----+---------+------------+
Notification:
+------------+----------+------+-----+---------+------------+
| Field | Type | Null | Key | Default | Extra |
+------------+----------+------+-----+---------+------------+
| Id | int(11) | NO | PRI | | auto_incre |
| Gender | int(11) | YES | | | |
| HairColor | int(11) | YES | | | |
| EyeColor | int(11) | YES | | | |
| Company | int(11) | YES | | | |
| Date | datetime | NO | | | |
+------------+----------+------+-----+---------+------------+
Initial idea
The initial idea I had was doing a select for each possibility and joining via union:
-- Selects new users by gender notification
SELECT *
FROM Notification
inner join User on (
User.Date >= Notification.Date and
Notification.Gender = User.Gender and
Notification.HairColor is null and
Notification.EyeColor is null and
Notification.Company is null
)
union all
-- Selects new users by gender and hair color notification
SELECT *
FROM Notification
inner join User on (
User.Date >= Notification.Date and
Notification.Gender = User.Gender and
Notification.HairColor = User.HairColor and
Notification.EyeColor is null and
Notification.Company is null
)
-- ... and so on, doing a select for each option, resulting in 16 selects (4 columns: gender, hair color, eye color and company)
My question is:
Is there another way I can do this SQL querying all the possibilities of notifications in a more easy?
Following this structure of 4 columns we already have 16 selects. In my real structure will have more columns with something unfeasible to keep it that way.
Is there any other suggestion storage structure of the data for a better way to do this functionality?
SELECT *
FROM Notification
inner join User on (
User.Date >= Notification.Date and
(Notification.Gender is null or Notification.Gender = User.Gender) and
(Notification.HairColor is null or Notification.HairColor = User.HairColor) and
(Notification.EyeColor is null Notification.EyeColor = User.EyeColor) and
(Notification.Company is null or Notification.Company = User.Company)
)
This way you get every set of user with the notification stored in the tables.
This is the way I would implement this user registration / notification functionality:
Three tables: Users, Notif_type, Notif_queue.
A trigger on insert on table Users which calls a stored procedure SendNotification(user_id).
The stored proc will have the logic which you can change overtime without having to modify the schema/data. The logic will be:
to select the type of notification (form Notif_type) the new user should receive based on your rules;
to insert a row in Notif_queue which holds a FK to user_id and notif_type_id, so that the functionality notifying the user is completely de-coupled from the notification rules.
why can't you just use the one table "user" and put an extra field/flag called [Notified] so that every time you want to send notifications just refer it to the flag.
i find it irrelevant to use the notification table.

MySQL syntax for Join Update

I have two tables that look like this
Train
+----------+-------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+----------+-------------+------+-----+---------+-------+
| TrainID | varchar(11) | NO | PRI | NULL | |
| Capacity | int(11) | NO | | 50 | |
+----------+-------------+------+-----+---------+-------+
Reservations
+---------------+-------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+---------------+-------------+------+-----+---------+----------------+
| ReservationID | int(11) | NO | PRI | NULL | auto_increment |
| FirstName | varchar(30) | NO | | NULL | |
| LastName | varchar(30) | NO | | NULL | |
| DDate | date | NO | | NULL | |
| NoSeats | int(2) | NO | | NULL | |
| Route | varchar(11) | NO | | NULL | |
| Train | varchar(11) | NO | | NULL | |
+---------------+-------------+------+-----+---------+----------------+
Currently, I'm trying to create a query that will increment the capacity on a Train if a reservation is cancelled. I know I have to perform a Join, but I'm not sure how to do it in an Update statement. For Example, I know how to get the capacity of a Train with given a certain ReservationID, like so:
select Capacity
from Train
Join Reservations on Train.TrainID = Reservations.Train
where ReservationID = "15";
But I'd like to construct the query that does this -
Increment Train.Capacity by ReservationTable.NoSeats given a ReservationID
If possible, I'd like to know also how to Increment by an arbitrary number of seats. As an aside, I'm planning on deleting the reservation after I perform the increment in a Java transaction. Will the delete effect the transaction?
Thanks for the help!
MySQL supports a multi-table UPDATE syntax, which would look approximately like this:
UPDATE Reservations r JOIN Train t ON (r.Train = t.TrainID)
SET t.Capacity = t.Capacity + r.NoSeats
WHERE r.ReservationID = ?;
You can update the Train table and delete from the Reservations table in the same transaction. As long as you do the update first and then do the delete second, it should work.
Here is another example of an UPDATE statement that contains joins to determine the value that is being updated. In this case, I want to update the transactions.payee_id with the related account payment id, if the payee_id is zero (wasn't assigned).
UPDATE transactions t
JOIN account a ON a.id = t.account_id
JOIN account ap ON ap.id = a.pmt_act_id
SET t.payee_id = a.pmt_act_id
WHERE t.payee_id = 0