So I believe I need a Inner Join for this query, but am not 100% sure.
First of all see my database diagram:
Click here for database diagram
What I'm trying to achieve:
I'm trying to get all messages (so user_username, text, posted_at FROM Messages) where the user_username matches with the followed_username, and where the followed_username has a follower_username that = ?.
So essentially, everyone followed by ?, I want to get all their messages.
Where ? = an inputed username
What I've tried so far
I've tried a number of sql statements and have thus far been unable to get be successful. These are some I have tried.
$sql = "SELECT user_username, text, posted_at FROM Messages, Users, User_Follows WHERE user_username = (SELECT username FROM Users WHERE username = (SELECT followed_username FROM User_Follows WHERE follower_username = ?)) ORDER BY posted_at DESC;";
$sql = "SELECT user_username, text, posted_at FROM Messages, User_Follows WHERE follower_username = ? AND followed_username = user_username;";
$sql = "SELECT user_username, text, posted_at FROM Messages JOIN User_Follows ON user_username = followed_username WHERE follower_username = followed_username;";
I now think I need to use an inner join to achieve what I want, but am not too sure whether this is correct, or how to go about it.
Thanks in advance.
Try something like:
SELECT M.user_username, M.text, M.posted_at
FROM Messages M
INNER JOIN Users U on M.user_username= u.username
INNER JOIN User_Follows UF on UF.followed_username = u.username
WHERE UFfollower_username = ?
ORDER BY posted_at DESC;
Note for future designs that using a long varchar or nvarchar field is a poor choice for a field you will be joining on. Further, if the field is a character type field, it is often subject to being changed over time which is also a bad choice for a key field.
Integer joins are much faster generally. It might be ok if it can save you from having to to do some joins but in general it is a poor idea.
If someone changed his username (which does happen in every system I have ever worked in) you would then have to update all the child tables which could be quite a lot of records to update. If you used a surrogate key, you would only need to update the parent table.
Additionally the word text is a reserved word for many databases and it is best to avoid those in naming fields.
a simple sub query should do it:
select * from messages where user_username in
(
select followed_username from user_follows
where follower_username = 'your_input_username'
);
Related
I'm working through MySQL connector in python on a project where I'm analyzing books.
I would gladly accept any help with my issue (explained below).
The relevant DB structures:
each Word, in each book, has its own word_id(primary key) and text.
each Word_instance has word_id, word_serial, offset in line, sentence number and so on...
the entity Word_instance's word_serial is its offset from the beginning of the book.
each Phrase has its own id and text.
each Phrase_word has phrase_id and word_id(from above).
Right now, I'm trying to figure out how to build a query that will locate a phrase from the user in the database.
Words are a part of a phrase if they have consecutive word_serial and are in the same sentence.
so far I've managed to build the following mess of a query:
select book_id
, word_txt
, word_serial
, sentence_serial
, ROW_NUMBER() Over (partition by sentence_serial, book_id) as encounter_num
from word
join word_instance
on word.word_id = word_instance.word_id
join word_in_phrase
on word.word_id = word_in_phrase.word_id
where phrase_id = %s
order
by book_id
, sentence_serial
, word_serial
In the following table image is the result set of said query.
let's say the user has entered the phrase: "I believe in cause".
in that case I would need to extract word_serial = 562, as it is the beginning said phrase.
can I accomplish such a task without extracting row by row and assessing whether the current row is part of the phrase and in the correct order?
In fact, there are way to many rows to examine outside of SQL to consider that a possibility.
I will appreciate your help immensely, as I'm stuck on this issue for far too long...
As requested, I'm uploading images of relevant DB entities:
Word_in_phrase entity
Word_instance entity
word entity
This probably isn't the most efficient way of writing this, but I think it works in principle and you could tinker with it as you wanted. Note that I assumed phrases can't cross sentence boundaries (wi2.sentence_serial = wi1.sentence_serial) and I've assumed a column word_in_phrase.order_id exists that starts at 0 and increases by 1 for each word. I'm also assuming word_id increases by 1 each row. (You could make those assumptions true by using CTEs where that is true instead of the real tables).
with (
SELECT *
FROM word_in_phrase
WHERE phrase_id = %s
) as phrase
select book_id
, word_txt
, word_serial
, sentence_serial
from word
join word_instance wi1
on word.word_id = word_instance.word_id
where (SELECT COUNT(*) FROM phrase) = (SELECT COUNT(*) FROM word_instance wi2 INNER JOIN phrase on wi2.word_id = phrase.word_id WHERE wi2.book_id = wi1.book_id and wi2.sentence_serial = wi1.sentence_serial and wi2.word_id = wi1.word_id + phrase.order_id)
order
by book_id
, sentence_serial
, word_serial
Alternatively, you might prefer something like
with (
SELECT *
FROM word_in_phrase
WHERE phrase_id = %s
) as phrase
select wi1.book_id
, word_txt
, wi1.word_serial
, wi1.sentence_serial
from word
join word_instance wi1
on word.word_id = word_instance.word_id
inner join word_instance wi2
on wi2.book_id = wi1.book_id and wi2.sentence_serial = wi1.sentence_serial
INNER JOIN phrase
on wi2.word_id = phrase.word_id
WHERE wi2.word_id = wi1.word_id + phrase.order_id
GROUP BY
wi1.book_id
, word_txt
, wi1.word_serial
, wi1.sentence_serial
HAVING COUNT(*) = (SELECT COUNT(*) FROM phrase)
ok this is hard to explain...
i have the following tables:
table : words
table: word_progress
table: word_set
foreign keys:
words.id = word_set.word_id
word_progress.word_id = words.id
word_progress.user_id = users.id
basically a word is in a word_set. word_progress (it keeps score on a certain word) relates to a user_id and word_id.
the problem is sometimes there is not an entry in word_progress for user_id and word_id. but when it IS there, i wanna be able to use word_progress in the WHERE part of the query. but if it is not there I dont. My work around at the moment is before running the statement, i do an "insert IGNORE IF EXISTS into work_progress (word_id,user_id) values (?,?)" to make sure its there
i have this query
select
words.* ,
word_progress.progress from words
left join word_progress on word_progress.word_id = words.id
left join word_set on word_set.id = words.word_set_id
where word_set.id = ? and word_progress.user_id = ?
but... the problem is sometimes there is no word_progress entry for that word
how can i do this?
you're already left-joining, so when there's no data available, you'll just get null as value for your word_progress-fields. just check for that like this:
...
and
(
word_progress.user_id is null
or
word_progress.user_id = ?
)
another way would be to add the user-restriction directly to the join-criteria like this:
...
left join word_progress
on word_progress.word_id = words.id
and word_progress.user_id = ?
...
and drop that criteria from the where-part.
note that, in both cases, you'll have to handle the null-cases for progress later in your code properly.
yeah, this is ambiguous: " i wanna be able to use word_progress in the WHERE part of the query. but if it is not there I dont. "
in the left joins,
if there's no word_progress to match a word, you'll still get a result row for the word, it's just that all the fields of word_progress will be null.
Same for word_set: no corresponding word_set, then word_set.id and all the rest are null.
so include 'is null' or 'is not null' in your where clause depending on what you want.... think about that case, that's what you left ambiguous. or remember that 'a = b' is false if either a or b is null (even if they're both null), so design your where clause to fit.
this is my database
database schema http://slashdir.com/php/blogg/images/bloggdb.png
What i want to do, is, for a given userid, show the total times he has been reported.
I have read various other questions on the matter, but I'm still stumped.
The latest query i tried was
select
sum(posts.timesreported + comments.timesreported) AS total_reports
FROM
posts
INNER JOIN comments ON (posts.userid = comments.userid)
WHERE posts.userid=5 AND comments.userid=5;
But this must be wrong as the number i get is much too high
Thanks!
SELECT
CASE WHEN NULL
THEN 0
ELSE (select sum(posts.timesreported) AS total_posts_reports
FROM posts INNER JOIN users ON (posts.userid = users.id)
WHERE posts.userid=5)
END
+
CASE WHEN NULL
THEN 0
ELSE (select sum(comments.timesreported) AS total_comments_reports
FROM comments INNER JOIN users ON (comments.userid = users.id)
WHERE comments.userid=5)
END
FROM DUAL;
Instead of
sum(posts.timesreported + comments.timesreported) AS total_reports
try
sum(posts.timesreported) + sum(comments.timesreported) AS total_reports
and I think you need to group by userId
WHERE posts.userid=5 AND comments.userid=5; is unnecessary since the tables are joined.
And sum operator is not correct logically
Use this query
select
sum(posts.timesreported) + sum(comments.timesreported) AS total_reports
FROM
posts
INNER JOIN comments ON (posts.userid = comments.userid)
WHERE posts.userid=5
It looks like your collecting the sum PRIOR to singling out the user. Perhaps this is adding those column values for all users prior to the join? What happens if you SELECT *, perform your INNER JOIN where userid = 5. Save the column values as two variables and then try to add them. Do you get the same result?
This might help you error check to see if the above theory is accurate.
<?php
// Connects to your Database
mysql_connect("your.hostaddress.com", "username", "password") or die(mysql_error());
mysql_select_db("Database_Name") or die(mysql_error());
//Run Query
$NUM1=mysql_query("SELECT Field1 FROM Table WHERE user.key=5");
$NUM2=mysql_query("SELECT Field2 FROM Table WHERE user.key=5");
//Print Each Result
echo 'Num1 = '.$NUM1;
echo 'Num2 = '.$NUM2;
//Print Total
$TOTAL = $NUM1 + $NUM2;
echo 'Total = '.$TOTAL;
?>
I have a stored procedure which is building a dynamic sql query and then running it via exec(#sql).
The stored proc is joining about 12 tables. As it was, it was running relatively quickly. But then i needed to added in an additional field. To do this, i created a scalar function, which looks like this:
SELECT #weight = #weight +COUNT(*) FROM dbo.UserPDMedication WHERE UserID = #userid
SELECT #weight = #weight +COUNT(*) FROM dbo.[User] WHERE UserID = #userid AND HoehnYarhID IS NOT null
SELECT #weight = #weight +COUNT(*) FROM dbo.[User] WHERE UserID = #userid AND DateOfBirth IS NOT NULL
SELECT #weight = #weight +COUNT(*) FROM dbo.[User] WHERE UserID = #userid AND GenderID IS NOT NULL
SELECT #weight = #weight +COUNT(*) FROM dbo.[User] WHERE UserID = #userid AND DateDiagnosed IS NOT null
It's basically just a function that will return an int based on how many questions a user has filled out. So for each user in the stored proc, this function gets called. The stored proc looks like this:
SELECT DISTINCT u.UserID, u.Healthy, u.DateOfBirth, u.City, st.StateCode AS State, u.GenderID, g.Gender, u.Latitude, u.Longitude, u.PDConditionID, u.Zip, u.Distance,
(SELECT TOP 1 EmailID FROM Messages m WHERE TrialID = ' + #trialID + ' AND ToUserID = u.userid AND LocationID = ' + #locationID + ') AS MessageID, dbo.UserWeightedValue(u.UserID) as wt
FROM [User] u
INNER JOIN aspnet_UsersInRoles uir ON u.AspnetUserID = uir.UserId
INNER JOIN aspnet_Roles r ON uir.RoleId = r.RoleId
FULL JOIN UserHealthCondition uhc ON u.UserID = uhc.UserID
FULL JOIN UserMotorSymptom ums ON u.UserID = ums.UserID
FULL JOIN UserNonMotorSymptom unms ON u.UserID = unms.UserID
FULL JOIN UserPDMedication updm ON u.UserID = updm.UserID
FULL JOIN UserPDTreatment updt ON u.UserID = updt.UserID
FULL JOIN UserSupplement us ON u.UserID = us.UserID
FULL JOIN UserPDGeneticMarker updgm ON u.UserID = updgm.UserID
FULL JOIN UserFamilyMember ufm ON u.UserID = ufm.UserID
FULL JOIN State st ON u.StateID = st.ID
FULL JOIN Gender g ON u.GenderID = g.ID
WHERE u.UserID IS NOT NULL
(i removed some chunks to try and keep this short). This get's executed as a dynamic string in the stored proc. Any tips on how i can optimize this to speed things up?
Thanks
EDIT: i got this working using a combination of suggestions here. I kept my function as is although i combined the multiple select statements into 2 statements.I then took the original stored proc and changed the select to a select into ##temp. And then i ran my function against that temp table. Execution time dropped down to 3-4 seconds. I think I will have to give credit to grant for this question since it was his pointing out distinct that put me on the right trail. But thank you to everyone.
The DISTINCT is absolutely going to cause a performance hit as it does aggregations. Do you really need it? Frequently when you see DISTINCT it's an indication of a data or structural issue that is getting papered over by the ability to eliminate duplicates that the structure should elminate on it's own.
After that, instead of a correlated query in the SELECT list, I'd look to move that as a JOIN. It's not a sure fire win, but frequently the optimizer is better able to work that into the plan.
Based on the complexity of what you're presenting, I'd also look at the execution plan. First thing to check, do you have a full optimization or did it timeout. If it timed out, then you're dealing with a best guess, not a fully calculated "good enough" plan. If that's so, you need to look at simpllifying this query. If you have a good enough plan, see where the bottlenecks are within it.
If UserID is the primary key of the table User, then there is no need to do one SELECT for question filled by the user, you can wrap it in just one SELECT:
SELECT #weight = #weight + COUNT(HoehnYarhID) + COUNT(DateOfBirth) + COUNT(GenderID) + COUNT(DateDiagnosed)
FROM dbo.[User]
WHERE UserID = #userid
Convert the scalar valued function into an inline table valued function.
Scalar functions, inlining, and performance
On my website, I have a method that allows a logged in user to mark articles as a favourite, when logged in the articles are highlighted as being saved as a favourite, however if the user has no favourites, I cannot get the query to return any data, what is wrong with my query?
SELECT `job_id`,
COUNT(jobs.job_id) as jobs,
`employers`.`employer_id`,
`logo_small`, `logo_large`,
`company_name`, `job_tags`,
`favourite_employers`.`employer_id` AS employer
FROM (`employers`)
LEFT JOIN `jobs` ON `employers`.`employer_id` = `jobs`.`employer_id`
JOIN `favourite_employers` ON `favourite_employers`.`employer_id` = `jobs`.`employer_id`
WHERE `favourite_employers`.`user_id` = '2'
GROUP BY `jobs`.`employer_id`
ORDER BY `jobs`.`job_id` DESC
use LEFT JOIN instead of JOIN for the favourite_employers table
I know this may seem silly, but did you end the query with a ;?
It would be good to see the DB schema but you probably need a LEFT JOIN for favourite_employers.
The reason could be because this join:
JOIN `favourite_employers` ON `favourite_employers`.`employer_id` = `jobs`.`employer_id`
is returning nothing because there is no favorites