Retrieving "likes" tied to users from a database - php

I'm new to database structure. I'm trying to create an app that allows users to like certain entries, but I want to be able to tie likes to users so that I can change the visuals before/after the like action.
I think from research that I should have an 'entries' and 'users' table and then have a 'likes' table that ties the two to each other.
The only thing I'm unsure of is, when getting and displaying the contents... how would I write the queries? If I query for all the entries I need, do I then go back and individually query each to see if it has a like tied to it for the current user? That seems like it might be a costly operation. Is there a more efficient way?
Hope that makes sense,
Thanks.

I think you have the right database design in mind. As far as queries are concerned, assume tables as such:
Users
ID | Name
1 | Bob
2 | Sally
Entries
ID | Name
1 | Red
2 | Blue
3 | Yellow
Likes
UserID | EntryID
1 | 1
1 | 2
2 | 2
2 | 3
So we can say Bob likes Red and Blue while Sally likes Blue and Yellow. So a query to retrieve all entries, plus an indicator of what Bob likes would be:
SELECT
e.ID,
e.Name,
l.UserID
FROM Entries e LEFT JOIN Likes l ON l.EntryID = e.ID
WHERE l.UserID = 1 -- Bob's User ID
ORDER BY e.Name
This would return
ID | Name | UserID
2 | Blue | 1
1 | Red | NULL
3 | Yellow | 1
The UserID column indicates if Bob likes the entry or not - a NULL is No and a value is Yes.

Assuming you have a table Entries with a column entity_id (and whatever else you store about the entity) and a second table UserLikes that contains the columns user_id and entity_id, you would do the following:
SELECT Entries.col1, Entries.col1 . . ., UserLikes.user_id
FROM Entries LEFT OUTER JOIN UserLikes ON
Entries.entity_id = UserLikes.entity_id
WHERE UserLikes.user_id = :user_id
AND Entity.col_whatever = :whatever
In this example, Entries.col1, Entries.col2 . . . is the list of columns you want to get back about the Entries. The :user_id is a parameter that contains the id of the user you're currently trying to display Entries for. And the last line is standing in for whatever limitations you want to put on the Entries are returned.
This query will give you a row for each Entry you searched for. You can check the value the returned column user_id. If it's NULL then it was not liked by the user, if it contains the user's id, it was liked by the user.

I think u can retrieve the entries and query the likes table at the same time to get if the current user likes the entry performing a stored procedure. So u can control the value of the set of data returned by the query for example returning one colum for the entry text and one boolean column to evaluates the current user likes... In this way you will at least one parameter for the stored procedure to indicate who is the current user
I hope this idea help u...

Related

Filter MySQL records between two tables by Join depending on a specific column value

Recently I've been working on a PHP/MySQL script which reads information from a database with user info and file info stored in two separate tables.
Below are the schema's for the tables.
Table1
UID | Username | PermissionLevel
1 | First | 1
2 | Next | 3
3 | More | 2
Table2
FID | Filename | FileLevel | UploadUsername
1 | file.txt | 2 | First
2 | hand.mp4 | 1 | First
3 | 1245.dds | 1 | Next
4 | beta.sql | 3 | More
For the purpose of this message I have omitted the passwords column and the file title/description column, since they play no part in the result I am trying to achieve.
So far I have come up with this SQL code
SELECT DISTINCT table2.*,
table1.*
FROM table2 JOIN table2 ON table2.FileLevel <= table1.PermissionLevel
WHERE table2.UploadUsername = table1.Username
ORDER BY FID DESC LIMIT 7
This generates the appropriate listing I want, but does not filter the level of content shown.
Any user with a PermissionLevel of 1 should only see Files with a FileLevel of 1. Users with PermissionLevel of "2" can see files of FileLevel of both 2 AND 1, and so on.
But at the current stage it seems to just want to display ALL results regardless of File/Permission Level.
I've been stuck at this issue for a couple of days now and just can't seem to get my head around this.
It's likely to be something simple I may have overlooked, but I hope that a fresh pair of eyes may help me.
I'm not sure if I understand the question fully, but this is what I could make of it. You have a query that returns all uploaded files. You now want to filter that list, so it sometimes shows less results, depending on the user's permission level. Please note that this user is the active user on the website, who is not necessarily the same user who uploaded a file, so this condition does not work on the table1 table of your query.
A common solution to this is to have a session variable storing your current user's id, and possibly other information.
$_SESSION['user'] = 'somebody';
$_SESSION['permissionLevel'] = 3;
If you don't have permissionLevel in a local variable, you will have to join on the table1 table twice, once to find the uploader and once to find the permission level of the current user. You also have a typo in your original query. The following will give you both users
SELECT *
FROM table2
JOIN table1 uploader WHERE table2.UploadUsername=table1.Username
JOIN table1 currentuser WHERE table2.PersmissionLevel<=table1.PermissionsLevel AND users.Username='$_SESSION[user]'
ORDER BY FID DESC

How to get values of other fields if a row is having duplicate entries in sql

I have a table like this:
ID build1 build2 test status
1 John ram test1 pass
2 john shyam test2 fail
3 tom ram test1 fail
The problem that I am facing is - on one of my webpage, only the values from the column "uild1" are available to me. Now in table there are 2 entries corresponding to "John". so, even if the user selects different "John", its showing the values for other values from the row only. On my webpage, in the drop down list, user can see 2 "John" but since query has been made using "John" condition, on both occasions, its showing the results from the first row only.
Try this:
SELECT t1.*
FROM Table1 t1
WHERE t1.build1 NOT IN(SELECT t2.build1
FROM table1 t2
GROUP BY t2.build1
HAVING COUNT(t2.build1) > 1);
SQL Fiddle Demo
This will give you only:
| ID | BUILD1 | BUILD2 | TEST | STATUS |
-----------------------------------------
| 3 | tom | ram | test1 | fail |
Since, it is the only row that has no duplicate build1.
If I'm understanding your question correctly, given a web page with 2 johns available to click on, how can you get each result accordingly? Unfortunately, there is no way of doing this with just SQL.
In your PHP code, if you can pass a parameter to your SQL code with either the ID or a counter/row number, then you could query the database to return a corresponding unique record.
Good luck.
You build1 is not unique or primary key so it is picking all the row matching your condition. You should use primary key or unique key to find the result. In your select drop-down your option value should be uniq/primary key so when you select particular "John" it will get result of that john.
select * from table_name where id=params[:id] ;
If you post some more information. It will be helpful to write better code for you.
select * from yourtable where build1 == 'john' limit 1;

MySQL query order by "most completed fields"

I have a table witch has 45 columns but only a few of these are yet completed. This table is continuously updated and added etc. In my auto-complete function i want to select these records ordered by the most completed fields(i hope you understand)?
One of the solutions is to create another filed (the "rank" field) and create a php function that selects * the records and gives a rank for each record.
... but i was wondering if there is a more simple way of doing this only whit a single ORDER BY?
MySQL has no function to count the number of non-NULL fields on a row, as far as I know.
So the only way I can think of is to use an explicit condition:
SELECT * FROM mytable
ORDER BY (IF( column1 IS NULL, 0, 1)
+IF( column2 IS NULL, 0, 1)
...
+IF( column45 IS NULL, 0, 1)) DESC;
...it is ugly as sin, but should do the trick.
You could also devise a TRIGGER to increment an extra column "fields_filled". The trigger costs you on UPDATE, the 45 IFs hurt you on SELECT; you'll have to model what is more convenient.
Note that indexing all fields to speed up SELECT will cost you when updating (and 45 different indexes probably cost as much as a table scan on select, not to say that the indexed field is a VARCHAR). Run some tests, but I believe that the 45-IF solution is likely to be the best overall.
UPDATE:
If you can rework your table structure to normalize it somewhat, you could put the fields in a my_values table. Then you would have a "header table" (maybe with only a unique ID) and a "data table". Empty fields would not exist at all, and then you could sort by how many filled fields are there by using a RIGHT JOIN, counting the filled fields with COUNT(). This would also greatly speed up UPDATE operations, and would allow you to efficiently employ indexes.
EXAMPLE (from table setup to two normalized tables setup):
Let us say we have a set of Customer records. We will have a short subset of "mandatory" data such as ID, username, password, email, etc.; then we will have a maybe much larger subset of "optional" data such as nickname, avatar, date of birth, and so on. As a first step let us assume that all these data are varchar (this, at first sight, looks like a limitation when compared to the single table solution where each column may have its own datatype).
So we have a table like,
ID username ....
1 jdoe etc.
2 jqaverage etc.
3 jkilroy etc.
Then we have the optional-data table. Here John Doe has filled all fields, Joe Q. Average only two, and Kilroy none (even if he was here).
userid var val
1 name John
1 born Stratford-upon-Avon
1 when 11-07-1974
2 name Joe Quentin
2 when 09-04-1962
In order to reproduce the "single table" output in MySQL we have to create a quite complex VIEW with lots of LEFT JOINs. This view will nonetheless be very fast if we have an index based on (userid, var) (even better if we use a numeric constant or a SET instead of a varchar for the datatype of var:
CREATE OR REPLACE VIEW usertable AS SELECT users.*,
names.val AS name // (1)
FROM users
LEFT JOIN userdata AS names ON ( users.id = names.id AND names.var = 'name') // (2)
;
Each field in our logical model, e.g., "name", will be contained in a tuple ( id, 'name', value ) in the optional data table.
And it will yield a line of the form <FIELDNAME>s.val AS <FIELDNAME> in the section (1) of the above query, referring to a line of the form LEFT JOIN userdata AS <FIELDNAME>s ON ( users.id = <FIELDNAME>s.id AND <FIELDNAME>s.var = '<FIELDNAME>') in section (2). So we can construct the query dynamically by concatenating the first textline of the above query with a dynamic Section 1, the text 'FROM users ' and a dynamically-built Section 2.
Once we do this, SELECTs on the view are exactly identical to before -- but now they fetch data from two normalized tables via JOINs.
EXPLAIN SELECT * FROM usertable;
will tell us that adding columns to this setup does not slow down appreciably operations, i.e., this solution scales reasonably well.
INSERTs will have to be modified (we only insert mandatory data, and only in the first table) and UPDATEs as well: we either UPDATE the mandatory data table, or a single row of the optional data table. But if the target row isn't there, then it must be INSERTed.
So we have to replace
UPDATE usertable SET name = 'John Doe', born = 'New York' WHERE id = 1;
with an 'upsert', in this case
INSERT INTO userdata VALUES
( 1, 'name', 'John Doe' ),
( 1, 'born', 'New York' )
ON DUPLICATE KEY UPDATE val = VALUES(val);
(We need a UNIQUE INDEX on userdata(id, var) for ON DUPLICATE KEY to work).
Depending on row size and disk issues, this change might yield an appreciable performance gain.
Note that if this modification is not performed, the existing queries will not yield errors - they will silently fail.
Here for example we modify the names of two users; one does have a name on record, the other has NULL. The first is modified, the second is not.
mysql> SELECT * FROM usertable;
+------+-----------+-------------+------+------+
| id | username | name | born | age |
+------+-----------+-------------+------+------+
| 1 | jdoe | John Doe | NULL | NULL |
| 2 | jqaverage | NULL | NULL | NULL |
| 3 | jtkilroy | NULL | NULL | NULL |
+------+-----------+-------------+------+------+
3 rows in set (0.00 sec)
mysql> UPDATE usertable SET name = 'John Doe II' WHERE username = 'jdoe';
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0
mysql> UPDATE usertable SET name = 'James T. Kilroy' WHERE username = 'jtkilroy';
Query OK, 0 rows affected (0.00 sec)
Rows matched: 0 Changed: 0 Warnings: 0
mysql> select * from usertable;
+------+-----------+-------------+------+------+
| id | username | name | born | age |
+------+-----------+-------------+------+------+
| 1 | jdoe | John Doe II | NULL | NULL |
| 2 | jqaverage | NULL | NULL | NULL |
| 3 | jtkilroy | NULL | NULL | NULL |
+------+-----------+-------------+------+------+
3 rows in set (0.00 sec)
To know the rank of each row, for those users that do have a rank, we simply retrieve the count of userdata rows per id:
SELECT id, COUNT(*) AS rank FROM userdata GROUP BY id
Now to extract rows in "filled status" order, we do:
SELECT usertable.* FROM usertable
LEFT JOIN ( SELECT id, COUNT(*) AS rank FROM userdata GROUP BY id ) AS ranking
ON (usertable.id = ranking.id)
ORDER BY rank DESC, id;
The LEFT JOIN ensures that rankless individuals get retrieved too, and the additional ordering by id ensures that people with identical rank always come out in the same order.

Select many relational ID in one shot(MYSQL)

so we have:
table users
id name password parent_id
the first user have the id 1, and others have the parent_id 1, so I select all the users that have the parent_id == 1 - they are the childs of the user with 1, okay its all right, but now i need to select the users that have the parent_id of the selected before users with they id, if they exists of course
user with id 1
/ | \
/ | \
/ | \
users with parent_id 1
user id 2 user id 3 id user 4
| | |
| | |
| | |
and here is the same, I need to select all the users that have the parent_id 2, 3, 4 for each of those user, its is something like a pyramide(triangle) from the top to the bottom
So the question is how can i make a query that will select it in one shot, not in many queries by extracting the id and then make other query - its not good i think
do you have an idea??
Here is a question, that covers your problem:
Is it possible to query a tree structure table in MySQL in a single query, to any depth?
Query below works only for finding children and grand-children of a single user and is a product of misunderstanding the question!
You could try joining user table on itself twice.
SELECT * FROM users as up
JOIN users as u on up.id=u.parent_id
JOIN users as uc on u.id=uc.parent_id
WHERE up.id={$grandParentUserId}
Aliases: up = user's parent, u = user, us = user's child.
Definitely not a pretty solution, but it's a single request.
I see you are using CI. You can have a look at this answer. Somewhat related to your question. You can select the users with NULL parent ID first and then populate their children
https://stackoverflow.com/a/9937130/876117

Best way for saving infinit playlists (arrays) into db? (php mySql)

So client gives me a string like "1,23,23,abc,ggg,544,tf4," from user 12 . There can be infinit number of elements with no spaces just value,value,... structure. I have users table (with users uId(key), names etc). I have streams table with ( sId(key), externalID, etc values). User sends me externalId's. And I need to hawe externalId's in play list (not my sId's). I need some way to store such array into my DB and be able to get it from DB.
I need to be able to do 2 things
return such string back to user
be able to get na array from it like {1; 23; 23; abc; ggg; 544; tf4;}
So what is best method (best here means shourt(small amount of) code)
to store such data into db
to retrivew stored tata in bouth ways shown
I think, something like this should work:
User: user_id
Value: user_id, value, id
And according to your example 1,23,23,abc,ggg,544,tf4 from user 12, you will have:
TABLE User
user_id
12
and
TABLE Value
user_id | value | id
12 | 1 | 0
12 | 23 | 1
12 | 23 | 2
12 | ggg | 4
12 | abc | 3
...
id will be used for ordering list for each user, so if you want do retrieve it, just use this query: SELECT value FROM VALUE WHERE user_id = 12 ORDER BY id
That's a classical one-to-many relationship. One user has many external ids:
User: id, name
UserExternal: user_id, id # both fields as PK, id as CHAR
To fetch every external id connected to the user just execute the following query:
SELECT u.id, u.name, ue.id AS external
FROM user u
LEFT JOIN user_external ue ON u.id = ue.user_id

Categories