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

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.

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.

MariaDB - insert record or update timestamp if record exists

Objective: cronjob runs a task; when completed successfully, insert new host record. If record exists, update timestamp to reflect this status.
# Table layout
> describe hosts_completed;
+-----------+---------------------+------+-----+-------------------+-----------------------------+
| Field | Type | Null | Key | Default | Extra |
+-----------+---------------------+------+-----+-------------------+-----------------------------+
| id | bigint(20) unsigned | NO | PRI | NULL | auto_increment |
| timestamp | timestamp | NO | | CURRENT_TIMESTAMP | on update CURRENT_TIMESTAMP |
| hostname | varchar(32) | YES | MUL | NULL | |
+-----------+---------------------+------+-----+-------------------+-----------------------------+
# Current inventory
> select * from hosts_completed;
+----+---------------------+----------+
| id | timestamp | hostname |
+----+---------------------+----------+
| 10 | 2020-11-02 12:51:08 | myHost1 |
| 11 | 2020-11-02 14:32:16 | MyHost2 |
+----+---------------------+----------+
I want to update the status for myHost1 and my best shot would be like
> insert into hosts_completed(hostname) values("myHost1") ON DUPLICATE KEY UPDATE timestamp=now();
and it runs but adds a new record, it does not update the myHost1 record.
Where is the glitch?
The on duplicate key syntax requires a unique constraint on the column that is used to detect the conflict. Create it first:
alter table hosts_completed
add constraint unique_hostname
unique (hostname);
Note that this pre-requires no duplicates in the column (otherwise you need to housekeep your data before you can create the constraint).
Then you can use your curent query:
insert into hosts_completed(hostname)
values('myHost1')
on duplicate key update timestamp = now();

SQL query to find the time difference

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

A good SQL Structure for grouped comments with replies

I have two tables: News, Images. Both could have comments, so i decided try to make a generic comments table. Also comments could have a reply. I solved two possible methods, but i dont know which choose in order of a good practice or good performance solution.
Method 1 (which i am using):
News:
| ID | CommentGroup | Content | ...etc
Images:
| ID | CommentGroup | Url | ...etc
Considering the next image:
| 14 | 22 | http://image.gif | ...etc
Where the comments could be these:
|UserA:
| Coment1
|
|--|UserB -> UserA:
| Coment2
|
|---|UserC -> UserB:
| | Comment4
|
|UserD -> UserA:
| Coment3
Resulting Comments:
| ID | Group | ReplyGroup | Replied | Content | User |
| 13 | 22 | NULL | 1 | Comment1 | UserA |
| 17 | 22 | 13 | 1 | Comment2 | UserB |
| 11 | 22 | 13 | NULL | Comment3 | UserD |
| 15 | 22 | 17 | NULL | Comment4 | UserC |
If after commented Image14, is created a New, i decide the future comments group number by counting the max of the group column (22) so add 1 (23).
New:
| ID | CommentGroup | Content | ...etc
| 14 | 23 | A new | ...etc
Comments:
| ID | Group | ReplyGroup | Replied | Content | User |
| 22 | 23 | NULL | 1 | Comment1 | UserA |
| 30 | 23 | 22 | NULL | Comment2 | UserB |
Method 2
taken from this question:
News:
| ID | Content | ...etc
Images:
| ID | Url | ...etc
Comments:
| ID | Group | Type | ReplyGroup | Replied | Content | User |
Where type dintincts between News or Images Group.
how you think is better?
or what other solutions are possible?
Thanks.
Initially as a basic implementation I would treat everything as 'content' grouping common attributes.
CONTENT (
id int primary key,
created_on datetime,
created_by int
)
Then have more specific tables of the types of content
e.g.
NEWS (
content_id int primary key foreign key references content(id),
article nvarchar(max)
)
and
IMAGES (
content_id int primary key foreign key references content(id),
url varchar(1000)
)
and
COMMENTS (
content_id int primary key foreign key references content(id),
parent_id int foreign key references content(id)
root_id int foreign key references content(id),
level int,
text nvarchar(2000)
)
Each of these would have a 1:1 relationship with CONTENT.
COMMENTS would then reference other content 'directly' via the parent_id, the reference being either an image, news or indeed another comment.
The root_id in the COMMENTS would reference the actual image or news content (as would the parent_id of all 'top level' comments). This adds the overhead of maintaining the root_id (which shouldn't be too difficult) but will aid selecting comments for some content.
e.g.
-- get the article
SELECT *
FROM content
JOIN news
ON news.content_id = content.id
JOIN users
ON users.id = content.created_by
WHERE content.id = #news_id
-- get the comments
SELECT *
FROM content
JOIN comments
ON comments.content_id = content.id
JOIN users
ON users.id = content.created_by
WHERE comments.root_id = #news_id

How to sum values when joining tables?

<hyperbole>Whoever answers this question can claim credit for solving the world's most challenging SQL query, according to yours truly.</hyperbole>
Working with 3 tables: users, badges, awards.
Relationships: user has many awards; award belongs to user; badge has many awards; award belongs to badge. So badge_id and user_id are foreign keys in the awards table.
The business logic at work here is that every time a user wins a badge, he/she receives it as an award. A user can be awarded the same badge multiple times. Each badge is assigned a designated point value (point_value is a field in the badges table). For example, BadgeA can be worth 500 Points, BadgeB 1000 Points, and so on. As further example, let's say UserX won BadgeA 10 times and BadgeB 5 times. BadgeA being worth 500 Points, and BadgeB being worth 1000 Points, UserX has accumulated a total of 10,000 Points ((10 x 500) + (5 x 1000)).
The end game here is to return a list of top 50 users who have accumulated the most badge points.
Can you do it?
My sample tables are:
user:
+-------+--------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------+--------------+------+-----+---------+-------+
| id | int(11) | YES | | NULL | |
| name | varchar(200) | YES | | NULL | |
+-------+--------------+------+-----+---------+-------+
badge:
+-------+---------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------+---------+------+-----+---------+-------+
| id | int(11) | YES | | NULL | |
| score | int(11) | YES | | NULL | |
+-------+---------+------+-----+---------+-------+
award:
+----------+---------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+----------+---------+------+-----+---------+-------+
| id | int(11) | YES | | NULL | |
| user_id | int(11) | YES | | NULL | |
| badge_id | int(11) | YES | | NULL | |
+----------+---------+------+-----+---------+-------+
Thus the query is:
SELECT user.name, SUM(score)
FROM badge JOIN award ON badge.id = award.badge_id
JOIN user ON user.id = award.user_id
GROUP BY user.name
ORDER BY 2
LIMIT 50
No, that's not the worlds most challenging query. Something simple like this should do it:
select u.id, u.name, sum(b.points) as Points
from users u
inner join awards a on a.user_id = u.id
inner join badges b on b.id = a.badge_id
group by u.id, u.name
order by 2 desc
limit 50