How would you store in database who's liked something? - sql

How should database store whos liked a certain post, should I have a seperate table which keeps tracks of all likes, and keeps every like of every post together, storing article_id, user_id, and like/dislike?
Thanks!

You are describing a N-M relationship between users and posts, where each user might ligke serveral posts and each post can be liked by several users.
I would recommend using a bridge table, with foreign keys refering the posts and users tables.
In a nutshell, that would look like:
table: users
user_id
name
...
table: posts
post_id
title
...
table: users_like_posts
user_id -- foreign key to users(user_id)
post_id -- foreign key to posts(post_id)
like_dislike

One option could be creating a table which trucks likes and dislikes:
Example:
Like and dislike could be boolean or anything else that you like to implement.
Table:Like
-----------------
id | post_id | user_id | like_dislike |
1 | 1234 | 13783 | 0 |
2 | 1234 | 12083 | 1 |

Related

PostgreSQL: Delete a row and all of it's references (FK) existing in others tables?

Let's say i have a table named "users" like this:
+----+------------+-----------+
| id | first_name | last_name |
+----+------------+-----------+
| 1 | 'Sid' | 'Barrett' |
| 2 | 'Roger' | 'Waters' |
| 3 | 'Richard' | 'Wright' |
| 4 | 'David' | 'Gilmour' |
| 5 | 'Nick' | 'Mason' |
+----+------------+-----------+
Each "user" have lots of references on several tables with the same column name "user_id" as a FK.
If i need to delete an element i believe the procedure is to delete the relations first (to avoid the "violates foreign key constraint" error) and then the element itself in "users" table, right?
But... Is there any possibility to delete a "user" element with all its references on all the others tables?
I'm working with NodeJS (Express) and using PostgreSQL.
Please apologize if the answer is obvious, I'm a newbie in SQL.
Thanks a lot in advise!!
Yes, use on delete cascade on the foreign keys.
create table users (
id bigserial primary key
...
);
create table posts (
...
user_id bigint not null references users on delete cascade
...
)
Now when a user is deleted, all their associated posts will also be deleted.
This will go on, for example, if a post has comments...
create table comments (
...
post_id biging not null references posts on delete cascade
...
)
When a user is deleted their posts will be deleted, and those posts' comments will be deleted. That's the "cascade" part.

How to dump selected PostgreSQL data from one database to other databse

I have three tables with foreign key relationship with each other.
The table school will be uploaded manually. A student will login to the website and check their marks
The entire data is to be uploaded to another new database of different instance
The Login Id(stud_id) of the student in DB1 is 10 and Login Id(stud_id) of the student in DB2 is 1 in another instance.
For retaining the data of student_marks table, I intend to do the following steps,
1. Dump student_marks table from DB1
2. Copy it to DB2
NOTE: stud_id would be different for both the databases
Is there any way to do the above.
Refer the table below,
school:
id | name| place
-----+-------------
1 | sch1 | test
student:
id | school_id| stud_name
-----+-------------
1 | 1 | stud1
student_marks:
id | stud_id| subj1 | subj2
-----+-----------------------
1 | 1 | 30 | 30
Thanks in advance!
First Disable the foreign key constraint, then dump the data and after that again enable the foreign key constraint or you can put foreign key constraint after migrating the data.

nullable foreign key columns denormalized table vs many normalized tables

In our entitlement framework each "resource" (resource is nothing but any entity that you want to protect by assigning privileges to roles which can access or not access based on privileges) is stored in a resource table like below.
DESIGN1
RESOURCE TABLE
id (int) | namespace (varchar) | entity_id | black_listed (boolean)
1 | com.mycompany.core.entity1 |24 | false
2 | com.mycompany.core.entity2 |24 | false --note entity2
3 | com.mycompany.core.entity10 |3 | false -- note entity10
each resource in the table represent different entity e.g. entity1,entity2,..,entity10. basically that's nothing but entity1.id, entity2.id, entity3.id, ... and so on. because RESOURCE table keeps resources for all kinds of entity - entity_id column in RESOURCE table can't have proper foreign key relationship constraint. we are thinking to refactor this schema such as follow
DESIGN 2
RESOURCE TABLE
id | description | entity1_id | entity2_id | entity3_id | entity4_id | entity5_id | entity6_id | black_listed(boolean)
1 | com.mycompany.core.entity1|24 | null | null | null |null | null
2 | com.mycompany.core.entity2|null | 24 | null | null |null | null
now entity1_id will have a proper FK to entity1 table , entity2_id will have proper FK to entity2 and so on. downside of this approach is that this table will always have null values for all the columns BUT one. e.g. you can only have one entity resource per row. also having null seems to be anti pattern especially for FK relationship. One other way would be normalize the schema and create a resource table for each enitty type. but that will be pretty insane to maintain and quickly become a headache. not saying it's good or bad but doesn't look like a practical design.
is there a better way to design such a table where proper FK relatinoships are also maintained? or you'll endorse Design 2?
You need to create one table for all entities with id (surrogate primary key) or entity_type, entity_id as unique key.
id entity_type entity_id
1 entity1 24
2 entity2 24
Then you need to have only one column in RESOURCE referring to this table (say entities). Your RESOURCE table will look like as in the first example, but the difference is there will be only one entities table, not 10.

Creating a database. Design help needed

I am practicing python flask and I am going to make a very simple music site and am now creating the database.
I am new to databases so I just wanted help to see if the tables and relations are correct.
Also would I store multiple song ID's in the playlist_songs songID column?
userTable
userID (PK), username, email, password, level
songTable
songID (PK), songName, songArtist, songGenre, songDuration
playlistTable
playlistID (PK), userID (FK), playlistName, playlistDescription
playlist_songs
playlistID (FK), songID (FK)
As requested, I'm adding some collective info based on your question and comments.
Your design looks fine. As recommended by Rowland, it could perhaps use an order column. Something to order by. If you choose not to add this the songs will be returned in a somewhat random order for the playlist, or you could order by the SongId column and be guaranteed the same order at least (within a playlist). But it wouldn't be changeable.
You asked how data was entered in to the playlist_songs table:
SongTable
SongId | SongName | ...
-----------------------------
1 | Happy Bithday | ...
2 | Last Christmas | ...
3 | Christmas tree | ...
4 | Some song | ...
PlaylistTable
PlaylistId | PlaylistName | ...
-------------------------------------
1 | My Birthday songs | ...
2 | My Christmas songs | ...
3 | All my songs | ...
Playlist_songs
PlaylistId (FK) | SongId (FK)
-----------------------------
1 | 1
2 | 2
2 | 3
3 | 1
3 | 2
3 | 3
3 | 4
As you can see the Playlist_songs table can contain many playlists and many songs. If you query Playlist_songs for PlaylistId = 2 it will return SongId 2 & 3, and so on.
Currently, a primary key would have to be a constraint on the two columns (a compound key). This is also where you could add an Order column, or just add a stand alone primary key (Id for example) and order by that.

Complex SQL query for messaging app

I'm building a messaging feature in a Django 1.6.2 application using PostgreSQL 9.3.4. On a user's "Messages" home page, I'll show a list of conversations that a user has with other users. Each conversation "tile" or block will show the picture and name of the other user in that conversation, the date the last message in that conversation was sent, and the first 25 characters in that last message. I'll also show a small "reply" icon if the last message was sent by user who is viewing these conversations. I've got my query to the point where I can identify all of the conversations between the viewer and all the other users but I'm having trouble pulling in the fields I need from the User and Message tables.
My tables (shown at the bottom) are User, Message, and Conversation. Although I've implemented my table schemas so that there is a many-to-many relationship between users and conversations, in the beginning I'm going to create my interface so that a user can only send a message to one other user instead of multiple users.
When I run my query on the data shown below, what I'm trying to get back is the conversation and user IDs for users 3, 4, 5 along with their associated usernames, the last message in that conversation, who sent it, and the date it was sent. Instead, I'm getting the error:
ERROR: syntax error at or near "WHERE"
Can anyone help me fix this query? I'm more interested in speed than elegance.
Test case
Data in conversation_user linking table:
id | conversation_id | user_id
----+-----------------+---------
1 | 1 | 32
2 | 1 | 3 <- want this
3 | 2 | 32
4 | 2 | 4 <- want this
6 | 3 | 3
7 | 3 | 1
8 | 4 | 32
9 | 4 | 5 <- want this
10 | 5 | 7
11 | 5 | 9
Rows I want to return. Each message is last message in that conversation.
conversation_id | user_id | username | from_user | message | send_date
----------------+---------+-----------+-----------+---------+----------
1 | 3 | user3 | u3 or u32 | <msg3> | <date>
2 | 4 | user4 | u4 or u32 | <msg4> | <date>
4 | 5 | user5 | u5 or u32 | <msg5> | <date>
Query that isn't working:
SELECT cu.conversation_id,
cu.user_id,
au.username,
m.from_user,
m.message,
m.send_date
FROM conversation_user cu
INNER JOIN auth_user au ON cu.user_id = au.id
INNER JOIN message m ON cu.conversation_id = m.conversation_id
ORDER BY m.send_date DESC LIMIT 1
WHERE conversation_id IN
(SELECT conversation_id
FROM conversation_user
WHERE user_id = 32)
AND user_id != 32;
Table definitions
# auth_user
--------------+--------------------------+------------------------------
id | integer | not null default nextval(...
username | character varying(30) | not null
Referenced by:
TABLE "conversation_user" CONSTRAINT "conversation_user_user_id_fkey" FOREIGN KEY (user_id) REFERENCES auth_user(id) DEFERRABLE INITIALLY DEFERRED
TABLE "message" CONSTRAINT "message_from_user_id_fkey" FOREIGN KEY (from_user_id) REFERENCES auth_user(id) DEFERRABLE INITIALLY DEFERRED
# conversation
------------+--------------------------+--------------------------------
id | integer | not null default nextval(...
start_date | timestamp with time zone | not null
Referenced by:
TABLE "conversation_user" CONSTRAINT "conversation_id_refs_id_4344ca71" FOREIGN KEY (conversation_id) REFERENCES conversation(id) DEFERRABLE INITIALLY DEFERRED
TABLE "message" CONSTRAINT "message_conversation_id_fkey" FOREIGN KEY (conversation_id) REFERENCES conversation(id) DEFERRABLE INITIALLY DEFERRED
# conversation_user
-----------------+---------+--------------------------------------------
id | integer | not null default nextval(...
conversation_id | integer | not null
user_id | integer | not null
Foreign-key constraints:
"conversation_id_refs_id_4344ca71" FOREIGN KEY (conversation_id) REFERENCES conversation(id) DEFERRABLE INITIALLY DEFERRED
"conversation_user_user_id_fkey" FOREIGN KEY (user_id) REFERENCES auth_user(id) DEFERRABLE INITIALLY DEFERRED
# message
Column | Type |
-----------------+--------------------------+---------------------------
id | integer | not null default nextval(...
conversation_id | integer | not null
from_user_id | integer | not null
to_user_uid | integer | not null
message | text | not null
send_date | timestamp with time zone | not null
Foreign-key constraints:
"message_conversation_id_fkey" FOREIGN KEY (conversation_id) REFERENCES conversation(id) DEFERRABLE INITIALLY DEFERRED
"message_from_user_id_fkey" FOREIGN KEY (from_user_id) REFERENCES auth_user(id) DEFERRABLE INITIALLY DEFERRED
Fix syntax
Basically, you just need to move the WHERE condition to its proper place, like #Lamak commented:
SELECT ...
FROM conversation_user cu
INNER JOIN ...
WHERE conversation_id IN
(SELECT conversation_id
FROM conversation_user
WHERE user_id = 32)
AND user_id != 32
ORDER BY m.send_date DESC
LIMIT 1;
Make it fast
According to comment:
I'm trying to select the last message in each of the [...] conversations user 32 is having.
SELECT cu.conversation_id
, ufrom.username AS from_user
, uto.username AS to_user
, m.message
, m.send_date
FROM conversation_user cu
LEFT JOIN LATERAL (
SELECT from_user_id, to_user_id, message, send_date
FROM message m
WHERE m.conversation_id = cu.conversation_id
ORDER BY send_date DESC
LIMIT 1
) m ON TRUE
LEFT JOIN auth_user ufrom ON ufrom.id = m.from_user_id
LEFT JOIN auth_user uto ON uto.id = m.to_user_id
WHERE cu.user_id = 32;
Notes
A join is typically faster than an IN construct on a subquery, especially with big sets. But you don't need either. You have been overcomplicating things.
You could have a simpler query with DISTINCT ON, but I expect this one to be faster.
Details:
Select first row in each GROUP BY group?
Optimize GROUP BY query to retrieve latest record per user
DB design
The query assumes that (user_id, conversation_id) is UNIQUE - which you confirmed in the comment. Be sure to add an actual UNIQUE constraint, which provides the much needed index automatically.
An index on message on (conversation_id, send_date DESC) would help, too. Details:
Is a composite index also good for queries on the first field?
Assuming auth_user.id is the PK, so it would be indexed.
message.to_user_uid is probably supposed to be to_user_id - like from_user_id.
You probably want to add another FK to stay consistent:
"message_to_user_id_fkey" FOREIGN KEY (to_user_id) REFERENCES auth_user(id)
Not sure why you think you need DEFERRABLE INITIALLY DEFERRED. If you don't know you need this, remove it. It's for special purposes and makes regular operations more expensive.
If only two users can take part in the same conversation, it would be more efficient to remove conversation_user altogether and add user1 and user2 or similar to conversation - unless there are more attributes for each combination of user/conversation. Potentially simplify message, too. You only need a boolean information instead of from_user and to_user.
According to relational theory, conversation can be seen as a the implementation of many-to-many relationship between table auth_user and itself.