SELECT from various tables, with a list of indexes - php

Here is the scenario.
I'm developing a timeclock system; I have these tables:
-punch (id_punch,date)
-in1 (id_in1,time,id_punch,...)
-in2 (id_in2,time,id_punch,...)
.
-in6 (id_in6,time,id_punch,...)
-out1 (id_out1,time,id_punch,...)
-out2 (id_out2,time,id_punch,...)
.
-out6 (id_out6,time,id_punch,...)
My question is, how can I with only one query in PHP to get all values from in and out table, from a list of id_punch values, for example:
Get all punchs of September, or
Get all punchs of July to December,
I mean... from a list of id_punch between two dates, get all the results from the in, out table.
The only way I think is to do a query with each id_punch variable, but in a month its about 20-25 queries... to much?

To get all the data from the tables you'll need to join them with JOIN MySQL JOIN
But from what I can gather by looking at you tables, you probably should be thinking about making this into one table rather than the multiple tables you have here.

You really need to store all the in/out data in one table that is a child of punch:
CREATE TABLE punch (
id_punch SERIAL PRIMARY KEY,
punch_date DATE NOT NULL,
ip_address INT UNSIGNED NOT NULL
-- plus other attributes
) ENGINE=InnoDB;
CREATE TABLE inout (
id_punch BIGINT UNSIGNED,
in_time TIME NOT NULL,
out_time TIME NULL,
PRIMARY KEY (id_punch, in_time),
FOREIGN KEY (id_punch) REFERENCES punch (id_punch)
) ENGINE=InnoDB;
Now you can query very easily for all punches in September:
SELECT *
FROM punch LEFT OUTER JOIN inout USING (id_punch)
WHERE EXTRACT(YEAR_MONTH FROM punch_date) = '200909';

Your database schema is a little unclear, but if you're asking how to get the results corresponding to a list of ids you already have this should work (assuming your ids are 1,3,5,7,9)
SELECT * FROM table1, table2, table 3
WHERE table1.punch_id = table2.punch_id AND table2.punch_id = table3.punch_id AND table3.punch_id IN (1,3,5,7,9)
you'll probably need to modify it just make sure every table's punch_id is joined to that IN constraint

I can't use one table cause i have some informations in each punch, as ipaddress, and other information.
Neil, the answer was in my nose, i already saw a solution like yours, but my doubt is how to put the list in the query, answer for my own question = use a foreach() in php to "populate" this list...
Something like:
> SELECT * FROM table1, table2, table 3 WHERE table1.punch_id = table2.punch_id AND table2.punch_id = table3.punch_id AND table3.punch_id IN (<? foreach($query->results() as $row) echo $row->id_punch;?>)
im using codeigniter

Related

Speed-up/Optimise MySQL statement - finding a new row that hasn't been selected before

First a bit of background about the tables & DB.
I have a MySQL db with a few tables in:
films:
Contains all film/series info with netflixid as a unique primary key.
users:
Contains user info "ratingid" is a unique primary key
rating:
Contains ALL user rating info, netflixid and a unique primary key of a compound "netflixid-userid"
This statement works:
SELECT *
FROM films
WHERE
INSTR(countrylist, 'GB')
AND films.netflixid NOT IN (SELECT netflixid FROM rating WHERE rating.userid = 1)
LIMIT 1
but it takes longer and longer to retrieve a new film record that you haven't rated. (currently at 6.8 seconds for around 2400 user ratings on an 8000 row film table)
First I thought it was the INSTR(countrylist, 'GB'), so I split them out into their own tinyint columns - made no difference.
I have tried NOT EXISTS as well, but the times are similar.
Any thoughts/ideas on how to select a new "unrated" row from films quickly?
Thanks!
Try just joining?
SELECT *
FROM films
LEFT JOIN rating on rating.ratingid=CONCAT(films.netflixid,'-',1)
WHERE
INSTR(countrylist, 'GB')
AND rating.pk IS NULL
LIMIT 1
Or doing the equivalent NOT EXISTS.
I would recommend not exists:
select *
from films f
where
instr(countrylist, 'GB')
and not exists (
select 1 from rating r where r.userid = 1 and f.netflixid = r.netflixid
)
This should take advantage of the primary key index of the rating table, so the subquery executes quickly.
That said, the instr() function in the outer query also represents a bottleneck. The database cannot take advantage of an index here, because of the function call: basically it needs to apply the computation to the whole table before it is able to filter. To avoid this, you would probably need to review your design: that is, have a separate table to represent the relationship between movies and countries, which each tuple on a separate row; then, you could use another exists subquery to filter on the country.
The INSTR(countrylist, 'GB') could be changed on countrylist = 'GB' or countrylist LIKE '%GB%' if the countrylist contains more than the country.
Then don't select all '*' if you need only some columns details. Depends on the number of columns, the query could be really slow

Selecting title, description and keywords from MySQL database using PHP

I am coding a tiny search engine for my practice. I want to add up search functionality in it. I am trying to select all rows of questions table upon matching title, description and keywords.
I created the following 3 tables:
questions(id(PK), title, description)
keywords(id(PK), label);
questions_keywords(id(PK), question_id(FK), keyword_id(FK));
So far my SQL query looks like this:
SELECT q.* FROM question_keywords qk
JOIN keywords k ON qk.keyword_id=k.id
JOIN questions q ON qk.question_id=q.id
WHERE q.description LIKE '%javascript%'
OR
k.keyword_label LIKE '%java%'
In this query, i am selecting all the rows from questions table containing the substring java or javascript
Am I doing it right or there is a better way to do it??
Thanks in advance.
AS others mentioned I would add distinct. I would also reorder the tables. Functionally I don't think it matters it just bugged me... ha ha
SELECT DISTINCT
q.*
FROM
questions AS q
JOIN
question_keywords AS qk ON q.id = qk.question_id
JOIN
keywords AS k ON qk.keyword_id = k.id
WHERE
q.description LIKE '%javascript%'
OR
k.label LIKE '%java%';
As you can see in this DBfiddle
https://www.db-fiddle.com/f/pcVqcMm1yUoU6NdSHitCVr/2
The reason you get duplicates is basically called a Cartesian product
https://en.wikipedia.org/wiki/Cartesian_product
In simple terms is just a consequence of having a "Many to Many" relationship.
If you see in the fiddle I intentionally created this situation by what I added to the Bridge ( or Junction ) table question_keywords in the last 2 Inserts
INSERT INTO question_keywords (question_id,keyword_id)VALUES(4,1);
INSERT INTO question_keywords (question_id,keyword_id)VALUES(4,2);
The duplicate row, is simply because there are 2 entries for this table with the matching value of 4 for question_id. So these are only Duplicates in the sense that we are only selecting the fields form the questions table. If we included fields from the keywords table. Then one row would have a keyword or Java #1 while the other would have Javascript #2 as the keyword.
Hope that helps explain it.
A few other things to note:
You have a syntax error in the query you posted k.keyword_label LIKE '%java%' should be k.label LIKE '%java%' according to your table definition in the question.
Typically the Junction table should be a combination of both tables it joins ( which you almost did ) but the pluralization is wrong question_keywords should be questions_keywords it's a small thing but it could cause confusion when writing queries.
There is really not a need for a separate primary key for the Junction table.
If you notice how I created the table in the fiddle.
CREATE TABLE question_keywords(
question_id INT(10) UNSIGNED NOT NULL,
keyword_id INT(10) UNSIGNED NOT NULL,
PRIMARY KEY(question_id,keyword_id)
);
The primary key is a compound of the 2 foreign keys. This has the added benefit of preventing real duplicate rows from being made. For example if you tried this
INSERT INTO question_keywords (question_id,keyword_id)VALUES(4,1);
INSERT INTO question_keywords (question_id,keyword_id)VALUES(4,1);
With the setup I have it would be impossible to create the duplicate. You can still have a separate primary key (surrogate key), but you should create a compound unique index on those 2 keys in place of it.

Adding a Row into an alphabetically ordered SQL table

I have a SQL table with two columns:
'id' int Auto_Increment
instancename varchar
The current 114 rows are ordered alphabetically after instancename.
Now i want to insert a new row that fits into the order.
So say it starts with a 'B', it would be at around id 14 and therefore had to 'push down' all of the rows after id 14. How do i do this?
An SQL table is not inherently ordered! (It is just a set.) You would simply add the new row and view it using something like:
select instancename
from thetable
order by instancename;
I think you're going about this the wrong way. IDs shouldn't be changed. If you have tables that reference these IDs as foreign keys then the DBMS wouldn't let you change them, anyway.
Instead, if you need results from a specific query to be ordered alphabetically, tell SQL to order it for you:
SELECT * FROM table ORDER BY instancename
As an aside, sometimes you want something that can seemingly be a key (read- needs to be unique for each row) but does have to change from time to time (such as something like a SKU in a product table). This should not be the primary key for the same reason (there are undoubtedly other tables that may refer to these entries, each of which would also need to be updated).
Keeping this information distinct will help keep you and everyone else working on the project from going insane.
Try using an over and joining to self.
Update thetable
Set ID = r.ID
From thetable c Join
( Select instancename, Row_Number() Over(Order By instancename) As ID
From CollectionStatus) r On c.instancename= r.instancename
This should update the id column to the ordered number. You may have to disable it's identity first.

mysql join two tables likes and posts

Here is my php/MySQL task:
I have a table POSTS that contains num field that is the primary key and other information fields about the post (author, title, etc.). I also have a table LIKE that contains a userId field that is the primary key and a field POST that corresponds to the num field in posts. Given a specific userID, I need to get all of the rows from the POSTS table that the userId 'likes'.
Table 1 - posts
-num
-author
-title
Table 2 - likes
-userId
-postId
This is all in php so my first idea was to get all of the rows from the LIKES table where the userId matches the one given and store those rows in an array. Then I would iterate through the array and for each row I would search get the row of the POSTS table where postId=POSTS.num. However, this seems like it would be rather slow, especially since each iteration through the array would be a separate mysql query.
I am assuming there is a faster way. Would it be to use a temporary table or is there a better way to join the tables? I have to assume that both tables contain many rows. I am a mysql novice so if there is a better solution please explain why it is better. Thank you in advance for you help!
Try the following query:
SELECT
`posts`.*
FROM
`likes`
INNER JOIN
`posts` ON
`posts`.num = `likes`.postId
WHERE
`likes`.Userid = {insert user id here}
Depending on your schema (not sure if each record in 'likes' has to be unique, you may want to use the DISTINCT keyword on your select to filter out duplicates.
SELECT poli.* FROM (
SELECT po.* FROM posts po
JOIN likes li
ON li.postId = po.num
WHERE li.userId = '$yourGivenUserId'
) AS poli
$yourGivenUserId is the given userId.

Storing user activity? PHP, MySQL and Database Design

Ok so a user comes to my web application and gets points and the like for activity, sort of similar (but not as complex) as this site. They can vote, comment, submit, favorite, vote for comments, write description etc and so on.
At the moment I store a user action in a table against a date like so
Table user_actions
action_id - PK AI int
user_id - PK int
action_type - varchar(20)
date_of_action - datetime
So for example if a user comes along and leaves a comment or votes on a comment, then the rows would look something like this
action_id = 4
user_id = 25
action_type = 'new_comment'
date_of_action = '2011-11-21 14:12:12';
action_id = 4
user_id = 25
action_type = 'user_comment_vote'
date_of_action = '2011-12-01 14:12:12';
All good I hear you say, but not quite, remember that these rows would reside in the user_actions table which is a different table to the ones in which the comments and user comment votes are stored in.
So how do I know what comment links to what row in the user_actions?
Well I could just link to the unique comment_id in the comments table to a new column, called target_primary_key in the user_actions table?
Nope. Can't do that because the action could equally have been a user_comment_vote which has a composite key (double key)?
So the thought I am left with is, do I just add the primary keys in a column and comma deliminate them and let PHP parse it out?
So taking the example above, the lines below show how I would store the target primary keys
new_comment
target_primary_keys - 12 // the unique comment_id from the comments table
user_comment_vote
target_primary_keys - 22,12 // the unique comment_id from the comments table
So basically a user makes an action, the user_actions is updated and so is the specific table, but how do I link the two while still allowing for multiple keys?
Has anyone had experience with storing user activity before?
Any thoughts are welcome, no wrong answers here.
You do not need a user actions table.
To calculate the "score" you can run one query over multiple tables and multiply the count of matching comments, ratings etc. with a multiplier (25 points for a comment, 10 for a rating, ...).
To speed up your page you can store the total score in an extra table or the user table and refresh the total score with triggers if the score changes.
If you want to display the number of ratings or comments you can do the same.
Get the details from the existing tables and store the total number of comments and ratings in an extra table.
The simplest answer is to just use another table, which can contain multiple matches for any key and allow great indexing options:
create table users_to_actions (
user_id int(20) not null,
action_id int(20) not null,
action_type varchar(25) not null,
category_or_other_criteria ...
);
create index(uta_u_a) on users_to_actions(user_id, action_id);
To expand on this a bit, you would then select items by joining them with this table:
select
*
from
users_to_actions as uta join comments as c using(action_id)
where
uta.action_type = 'comment' and user_id = 25
order by
c.post_date
Or maybe a nested query depending on your needs:
select * from users where user_id in(
select
user_id
from
users_to_actions
where
uta.action_type = 'comment'
);

Categories