Writing a better code / query - php

In my website, user's can update and other can comment on the updates, as usual like other social networking websites..
What am doing :
query = fetches first name, last name, user id, update id, update post UNION same details for the user who has logged in i.e /* Updates Of Current Logged In User + Updates Of People He Is Following */
loop {
prints first name last name and update
}
later I introduced commenting feature so now what I do is in the loop I call a query and run a loop again for comments..
loop {
prints first name last name and update
commentquery = fetches firstname, last name, user id, comment, of the particulat post
again loop {
prints first/last name of the person who commented, and his comment
}
}
Now according to me I guess this can be accomplished with the SQL query, that when I retrieve posts, I can fetch it's comments along with it, or is this the right way? the problem is when the outer loop runs, the inner loop also runs to fetch the appropriate comments, so can I join tables to retrieve the comments related to posts/updates?

This is very difficult to answer without your database structure or actual SQL but I have tried to generalise as much as possible. What you are trying to do should be done via SQL with two queries and one loop, something like below.
Edit Your fine looping and re-executing the query. You CAN fetch all this data in one go with SQL but you will duplicate allot of information in each call. For scalability and readability I would stick with two queries and two loops
/** updated after op's comments **/
SELECT data, that, you, want, for, one, post
FROM posts AS a
INNER JOIN users AS b ON b.id = a.user_id
loop over query results () {
/** output the post data **/
/** Then select the users who have posted on this post **/
SELECT a.comments, b.username
FROM comments AS a
INNER JOIN users AS b ON b.id = a.id
INNER JOIN posts AS c ON c.id = a.post_id AND c.post_id = '[post id value]'
ORDER BY a.date_posted;
loop comments here {
/** output comments here **/
}
}

I think merging it in only query might be possible but not nice.
But if I understand correctly what you dislike is that the query for the comments is executed with a differen WHERE clause in the loop. You could eliminate that by fetching all the comments for all the updates you are showing in one query before querying the DB for the updates:
query = select all comments for all updates joined with usernames and whatnot
loop{
$comments[postid][] = data
}
After this you do your normal query for the updates and access the comments from the preloaded array instead of from the database. Not sure if this is better solution though. On the upside you have that there is only one query for the updates, and one for the comments, and not one for the updates and one for the comments of each update.(Like 2 queries instead of 11 when you have 10 updates). Not sure if the difference is really big.
The downside is, that the code you had before was a little more obvious, and so easier to read for someone else.

Related

Multiple queries or can be done in one?

I'm not very experienced with more advanced MySQL query stuff.. (mostly basic queries, return and parse response..etc)
However.. I am not clear on the correct approach when I need multiple things (responses) from the database.. Is there a way to get these things from the single query? or do I need to do a new query for each time?
Background:
I use PDO to do a SELECT statement
ie:
$getAllVideos_sql = "SELECT * as FROM $tableName WHERE active IS NOT NULL OR active != 'no' ORDER BY topic, speaker_last, title;";
$getAllVideos_stmt = $conn->prepare($getAllVideos_sql);
$getAllVideos_stmt->execute();
$getAllVideos_stmt->setFetchMode(PDO::FETCH_ASSOC);
$results = $getAllVideos_stmt->fetch(PDO::FETCH_ASSOC);
//parse as I see fit
This gives me my 'chunk of data' that I can pick apart and display as I want.
However.. I want to also be able to give some stats (totals)
For the total (distinct) 'topics'.. as well as total count for the 'titles' (should all be unique by default)
Do I need to do another query, prepare, execute, setFetchMode, fetch all over again?
Is this the proper way to do this? Or is there a way to crib off the initial commands that are already in play?
To be clear, I'm not really looking for a query... I'm looking to understand the proper way one does this.. when they need several pieces of data like I do? multiple queries and executions..etc?
Or maybe it can and -should- be done in one snippet? With an adjustment to the query itself to return sub select/queries info?
this isnt the correct syntax, because it only returns 1 record..(but the total topic count seems to be correct, even though I only get 1 record returned)
SELECT *, count(DISTINCT topic)as totalTopics, count(DISTINCT title)as totalTitles FROM $tableName;
Maybe this the more proper approach? Try to include these totals/details in the main query to pick out?
Hope this makes sense.
Thanks
I don't think you're going to get anything very clean that'll do this, however something like this might work:
SELECT * from $Table t
INNER JOIN (
SELECT COUNT(DISTINCT Topic) as TotalTopics FROM $Table
) s ON 1 = 1
INNER JOIN (
SELECT COUNT(DISTINCT Title) as TotalTitles FROM $Table
) f ON 1 = 1
WHERE ( Active IS NOT NULL ) AND Active != 'no'
Especially with web applications, many people are regularly doing counts or other aggregations somewhere along the way. Sometimes if it is a global context such as all topics for all users, having some stored aggregates helps rather than requerying all record counts every time.
Example. If you have a table with a list of "topics", have a column in there for uniqueTitleCount. Then, based on a trigger, when a new title is added to a topic, the count is automatically updated by adding 1. You can pre-populate this column by doing a correlated update to said "topics" table, then once the trigger is set, you can just have that column.
This also works as I see many times that people want "the most recent". If your system has auto-increment IDs in the tables, similarly, have the most recent ID created for a given topic, or even most recent for a given title/document/thread so you don't have to keep doing something like.
select documentID, other_stuff
from sometable
where documentID in ( select max( documentID )
from sometable
where the title = 'something' )
Use where these make sense then your optimization pull-downs get easier to handle. You could even have a counter per document "title" and even a most recent posting date so they can quickly be sorted based on interest, frequency of activity, whatever.

SQL: Deleting old records only so long as there are not newer matching records?

I've got a really big collection of data in a postgres database where I'd like to nuke data past a particular age... but I do not want it nuking the latest iteration of data from any given location & site combination.
Basically, I've got a really big table that has a location (bigint), site (bigint), readdate (bigint), and a little accompanying data (note: there will be multiple entries for a given site, location, and readdate - but anything on the same readdate is considered part of the same scan, and needs to be kept for a given location).
Currently, I've just got it set to get rid of all old records... but the possibility exists that a particular site and location combination will stop giving out data for a while, and I'd like to preserve the final state if that happens. I'm doing the SQL queries from php, so I'm pretty sure I could hack together some highly ugly code that finds the latest readdate for any given site & location combination, then either deletes stuff younger than that for that location, or deletes based on the calender limit (whichever gives the lesser date), but I'd prefer to put the decision-making workload in the SQL query, rather than having to first get a list of all location, site, and max(readdate) entries, then iterate over them in php making individual delete queries.
My current query (which doesn't do what I want, as it deletes everything before $limit) is declared by:
$query="DELETE FROM votwdata WHERE readdate < '".$limit."';";
any ideas for a good revision?
If I understand what you are trying to do, you have a number of fields that might be the same, and you want to keep the most recent record. Assuming you have a sequential ID or a created_at on each record, you can run a subquery to identify the records you want to delete. For example:
select max(id),data1,data2 from table group by data1,data2;
That will pull the most recent record for a unique data1 and data2. You can run that as an inline query, joining it back to the original table.
select t.* from table t, (select max(id) "id",data1,data2 from table group by data1,data2) t2
where t.id=t2.id;
That will give you the most recent records. You can do an left join and look at the null values to delete anything that you don't like.
select t.id,t2.id
from table t left join (select max(id) "id",data1,data2 from table group by 2,3) t2 on t.id=t2.id
where t2.id is null;
That gives you all the records that you want to delete.
Okay, that's the dirty way - refactor away.

Selecting just one row from a joined MySQL query

I am trying to make a social networking site in PHP/MySQL. I am currently developing status update and comments on status's system. I am trying to show all status of mine and comments on certain status. For doing that I have two tables: comment and user_status.
I have used this MySQL query,
SELECT * FROM user_status LEFT JOIN
comment ON id_status = comment.status_id
WHERE sender_id = '$id2'
OR receive_id = '$id2'
/* $id2 is my id */
I have successfully showed status and one comment. But the problem is, when the number of comments are more than one, then the status shows more than one times. How much same status will be showed depends on how much comments available on certain status. But I would like to be able to display same status only one time, and display more than one comments (if available) on certain status.
This isn't so much a PHP problem as it is confusion about how SQL joins work.
It sounds as if what you really want is not so much a join but a distinct set of records from two tables. Until your SQL skils develop a little more, consider simplifying things by making two queries -- one each for comment and user_status. Also consider requesting just the specific fields you're interested rather than using SELECT *.
Here is a visual explanation of different SQL joins, in case you want to pursue this with a single query.
I assume that you are not displaying the raw results from the query, but rather are piping them to an html page to display. Display only the most recent status in a textbox. then display thin a table or list an ordered list of the comments.
Your query is correct.

Problem with getting specific data based on several factors in MySQL

I have a system where a user is part of a series of "runs", to each "run", can be added courses, teachers(users), classes and so on.
Each teacher(user) has chosen his/her classes & courses.
Here's a run-down of the tables I have that are relevant:
lam_run - The run in it self.
lam_run_course - Relational table that shows what runs has what courses
lam_teacher_course - Relational table that shows which teacher has which courses
lam_run_teacher - Relational table that shows what teachers are in what courses
What I want to do is show each teacher which runs that are relevant to them (based on which courses they have selected seen in lam_teacher_course) but in which they are not already participating.
Here's the MySQL code I have so far that does not work:
$query_relevant_runs = "
SELECT DISTINCT
lam_run_course.run_id
FROM
lam_teacher_course,
lam_run_course,
lam_run, lam_run_teacher
WHERE
lam_teacher_course.user_id = '1'
AND
lam_teacher_course.course_id = lam_run_course.course_id
AND
lam_run_teacher.user_id != '1'";
Instead this code shows all runs that are relevant, but it doesn't exclude the runs the user is already in..
What can I do to fix this?
Ps. Sorry for bad title, no idea what I should've called it :S
Here is a link to part of the databases (the relevant part): Link!
I think what you're looking for is:
LEFT JOIN `lam_run_teacher` `lam_run_teach_exclude`
ON `lam_run_teacher_exclude`.`user_id` = `lam_teacher_user`.`user_id`
...
WHERE `lam_run_teacher`.`user_id` IS NULL
The LEFT JOIN takes your current query, and appends the additional data to it. However, unlike the INNER JOIN you are using now (using the kinda-strange multiple-from syntax), the LEFT JOIN does not limit your resultset to just those where there is data for the righthand side. The righthand columns will be NULL. By filtering on that NULL, you can find all runs that are interesting, and for which there is not yet a relation to the teacher.
Does this help?
I'd recommend always using the normal join syntax (INNER JOIN target on target.id = source.id) - that way you're more aware of the idea that there are other kinds of join as well, and all your joins will look identical. It takes some getting used to, but definitely helps when your queries get more complex.
Also, in your cross-referencing tables, you can drop the primary key columns. If the only purpose of a table is to define a link between two tables, make the primary key consist of the two keys you've got. Unless you want to be able to related the same teacher to a run multiple times...
OK, took me way longer than it should have, but here's the complete thing:
SELECT
DISTINCT `lam_run_course`.run_id
FROM
`lam_run_course`
INNER JOIN
`lam_teacher_course`
ON `lam_teacher_course`.course_id = `lam_teacher_course`.course_id
LEFT JOIN
`lam_run_teacher` ON (`lam_run_teacher`.`run_id` = `lam_run_course`.`run_id` AND `lam_run_teacher`.`user_id` = 3)
WHERE
`lam_teacher_course`.user_id = 3
and `lam_run_teacher`.`run_id` IS NULL

using joins or multiple queries in php/mysql

Here i need help with joins.
I have two tables say articles and users.
while displaying articles i need to display also the user info like username, etc.
So will it be better if i just use joins to join the articles and user tables to fetch the user info while displaying articles like below.
SELECT a.*,u.username,u.id FROM articles a JOIN users u ON u.id=a.user_id
OR can this one in php.
First i get the articles with below sql
SELECT * FROM articles
Then after i get the articles array i loop though it and get the user info inside each loop like below
SELECT username, id FROM users WHERE id='".$articles->user_id."';
Which is better can i have explanation on why too.
Thank you for any reply or views
There is a third option. You could first get the articles:
SELECT * FROM articles
Then get all the relevant user names in one go:
SELECT id, username FROM users WHERE id IN (3, 7, 19, 34, ...)
This way you only have to hit the database twice instead of many times, but you don't get duplicated data. Having said that, it seems that you don't have that much duplicated data in your queries anyway so the first query would work fine too in this specific case.
I'd probably choose your first option in this specific case because of its simplicity, but if you need more information for each user then go with the third option. I'd probably not choose your second option as it is neither the fastest nor the simplest.
It depends how much data the queries are returning - if you'll be getting a lot of duplicate data (i.e. one user has written many articles) you are better off doing the queries separately.
If you don't have a lot of duplicated data, joins are always preferable as you only have to make one visit to the database server.
The first approach is better if applicable/possible:
SELECT a.*, u.username, u.id
FROM articles a
JOIN users u ON u.id = a.user_id
You have to write less code
There is no need to run multiple queries
Using joins is ideal when possible
Get the articles with one query, then get each username once and not every time you display it (cache them in an array or whatever).

Categories