SQL to find users that the current user hasn't liked/disliked

The name of the pictureThe name of the pictureThe name of the pictureClash Royale CLAN TAG#URR8PPP





.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty margin-bottom:0;







up vote
1
down vote

favorite












I have this really long query, which in my opinion looks ugly next to all the tidy code in my project. I was wondering if there was a way to optimize it or make it shorter at least:



$query = <<<SQL
SELECT * FROM users
WHERE NOT EXISTS (
SELECT id
FROM profile_likes
WHERE profile_id = users.id
AND viewer_id = $my_user->id
)
AND NOT EXISTS (
SELECT id
FROM profile_dislikes
WHERE profile_id = users.id
AND viewer_id = $my_user->id
)
AND id != $my_user->id
SQL


This query is a part of an user filter which has the purpose of filtering the users to only those that the currently logged in user ($my_user) hasn't liked/disliked yet.







share|improve this question





















  • Be sure that those interpolated variables are not user input. If they could be, you need to use parameterization or you have no security.
    – Aluan Haddad
    Feb 11 at 4:51







  • 1




    None of the variables are user input.
    – Ivan Grigorov
    Feb 11 at 5:04






  • 1




    Roger, I was just checking, it's amazing how often people forget. Regarding your question, it seems MySQL is still crap and doesn't support many basic set operations. I think the best option is to use UNION and then do the sub-query, but someone may have a much better suggestion.
    – Aluan Haddad
    Feb 11 at 5:23







  • 1




    I appreciate the concern. It happens to me quite often to forget to parametrize quries. I'll follow your suggestion and look into using UNION. Thanks.
    – Ivan Grigorov
    Feb 11 at 5:59
















up vote
1
down vote

favorite












I have this really long query, which in my opinion looks ugly next to all the tidy code in my project. I was wondering if there was a way to optimize it or make it shorter at least:



$query = <<<SQL
SELECT * FROM users
WHERE NOT EXISTS (
SELECT id
FROM profile_likes
WHERE profile_id = users.id
AND viewer_id = $my_user->id
)
AND NOT EXISTS (
SELECT id
FROM profile_dislikes
WHERE profile_id = users.id
AND viewer_id = $my_user->id
)
AND id != $my_user->id
SQL


This query is a part of an user filter which has the purpose of filtering the users to only those that the currently logged in user ($my_user) hasn't liked/disliked yet.







share|improve this question





















  • Be sure that those interpolated variables are not user input. If they could be, you need to use parameterization or you have no security.
    – Aluan Haddad
    Feb 11 at 4:51







  • 1




    None of the variables are user input.
    – Ivan Grigorov
    Feb 11 at 5:04






  • 1




    Roger, I was just checking, it's amazing how often people forget. Regarding your question, it seems MySQL is still crap and doesn't support many basic set operations. I think the best option is to use UNION and then do the sub-query, but someone may have a much better suggestion.
    – Aluan Haddad
    Feb 11 at 5:23







  • 1




    I appreciate the concern. It happens to me quite often to forget to parametrize quries. I'll follow your suggestion and look into using UNION. Thanks.
    – Ivan Grigorov
    Feb 11 at 5:59












up vote
1
down vote

favorite









up vote
1
down vote

favorite











I have this really long query, which in my opinion looks ugly next to all the tidy code in my project. I was wondering if there was a way to optimize it or make it shorter at least:



$query = <<<SQL
SELECT * FROM users
WHERE NOT EXISTS (
SELECT id
FROM profile_likes
WHERE profile_id = users.id
AND viewer_id = $my_user->id
)
AND NOT EXISTS (
SELECT id
FROM profile_dislikes
WHERE profile_id = users.id
AND viewer_id = $my_user->id
)
AND id != $my_user->id
SQL


This query is a part of an user filter which has the purpose of filtering the users to only those that the currently logged in user ($my_user) hasn't liked/disliked yet.







share|improve this question













I have this really long query, which in my opinion looks ugly next to all the tidy code in my project. I was wondering if there was a way to optimize it or make it shorter at least:



$query = <<<SQL
SELECT * FROM users
WHERE NOT EXISTS (
SELECT id
FROM profile_likes
WHERE profile_id = users.id
AND viewer_id = $my_user->id
)
AND NOT EXISTS (
SELECT id
FROM profile_dislikes
WHERE profile_id = users.id
AND viewer_id = $my_user->id
)
AND id != $my_user->id
SQL


This query is a part of an user filter which has the purpose of filtering the users to only those that the currently logged in user ($my_user) hasn't liked/disliked yet.









share|improve this question












share|improve this question




share|improve this question








edited Feb 11 at 5:10









Jamal♦

30.1k11114225




30.1k11114225









asked Feb 11 at 4:33









Ivan Grigorov

255




255











  • Be sure that those interpolated variables are not user input. If they could be, you need to use parameterization or you have no security.
    – Aluan Haddad
    Feb 11 at 4:51







  • 1




    None of the variables are user input.
    – Ivan Grigorov
    Feb 11 at 5:04






  • 1




    Roger, I was just checking, it's amazing how often people forget. Regarding your question, it seems MySQL is still crap and doesn't support many basic set operations. I think the best option is to use UNION and then do the sub-query, but someone may have a much better suggestion.
    – Aluan Haddad
    Feb 11 at 5:23







  • 1




    I appreciate the concern. It happens to me quite often to forget to parametrize quries. I'll follow your suggestion and look into using UNION. Thanks.
    – Ivan Grigorov
    Feb 11 at 5:59
















  • Be sure that those interpolated variables are not user input. If they could be, you need to use parameterization or you have no security.
    – Aluan Haddad
    Feb 11 at 4:51







  • 1




    None of the variables are user input.
    – Ivan Grigorov
    Feb 11 at 5:04






  • 1




    Roger, I was just checking, it's amazing how often people forget. Regarding your question, it seems MySQL is still crap and doesn't support many basic set operations. I think the best option is to use UNION and then do the sub-query, but someone may have a much better suggestion.
    – Aluan Haddad
    Feb 11 at 5:23







  • 1




    I appreciate the concern. It happens to me quite often to forget to parametrize quries. I'll follow your suggestion and look into using UNION. Thanks.
    – Ivan Grigorov
    Feb 11 at 5:59















Be sure that those interpolated variables are not user input. If they could be, you need to use parameterization or you have no security.
– Aluan Haddad
Feb 11 at 4:51





Be sure that those interpolated variables are not user input. If they could be, you need to use parameterization or you have no security.
– Aluan Haddad
Feb 11 at 4:51





1




1




None of the variables are user input.
– Ivan Grigorov
Feb 11 at 5:04




None of the variables are user input.
– Ivan Grigorov
Feb 11 at 5:04




1




1




Roger, I was just checking, it's amazing how often people forget. Regarding your question, it seems MySQL is still crap and doesn't support many basic set operations. I think the best option is to use UNION and then do the sub-query, but someone may have a much better suggestion.
– Aluan Haddad
Feb 11 at 5:23





Roger, I was just checking, it's amazing how often people forget. Regarding your question, it seems MySQL is still crap and doesn't support many basic set operations. I think the best option is to use UNION and then do the sub-query, but someone may have a much better suggestion.
– Aluan Haddad
Feb 11 at 5:23





1




1




I appreciate the concern. It happens to me quite often to forget to parametrize quries. I'll follow your suggestion and look into using UNION. Thanks.
– Ivan Grigorov
Feb 11 at 5:59




I appreciate the concern. It happens to me quite often to forget to parametrize quries. I'll follow your suggestion and look into using UNION. Thanks.
– Ivan Grigorov
Feb 11 at 5:59










2 Answers
2






active

oldest

votes

















up vote
2
down vote



accepted










Do you really need to keep likes and dislikes in different tables?



Seems to me that these would be pretty much the same thing with a different value in one single field. So maybe a profile_reactions table instead. Doing this would simplify your data model down to two tables and probably be more scalable in the future, if/when you want to ad more reaction types.



SELECT * should typically be avoided. It makes it harder for readers of the code to understand what fields they have to work with in the result set (they would have to go look at database schema to understand this). It can be more bandwidth intensive than necessary. It you only want a few fields from a table with a large number of fields, why pull all the extra data? It can potentially make the code more fragile. Can a database schema change potentially introduce bugs in code?



Consider whether relational storage (i.e. MySQL) is right for this in the long term. Since you are going to be dealing with most of the records in these tables, this is a query that really could be problematic over time, getting slower and slower as your user base grows. It simply doesn't scale well. The primary question to ask is whether the likes/dislikes (reactions) have any meaning outside the context of an owning user. My guess is that they don't. And if they don't, why does this data need to be normalized into it's own table?



This data model is one that actually works quite well in NoSQL storage, where the data model might look like this:



// User

"id": 123,
"reactions_to_users":
234: "like",
456: "dislike",
789: "like",
...

"reactions_from_users":
234: "like",
456: "dislike",
111: "like",
,
...



So assuming that you would already have this user object loaded up earlier in code (like you do know). When you go to make your query against the data store to get the the un-liked/disliked users, you would simply need to get the user ids under reactions_to_users and make a query against the data store for all users outside of this set.



I think your actual SQL code in PHP looks good. I prefer using heredoc or nowdoc syntax like you are doing. I also prefer SQL the read more vertically than horizontally, breaking up at each SQL phrase like you have done. I wouldn't change your style here.






share|improve this answer




























    up vote
    3
    down vote













    Personally, I would just stick with what you have already got.



    You could try something like this, although too me it doesn't look any tidier.
    You would need to profile it against your database, to see if it performs any better.



    SELECT * 
    FROM users
    left outer join profile_likes on profile_likes.profile_id = users.id AND profile_likes.viewer_id = $my_user->id
    left outer join profile_dislikes on profile_dislikes.profile_id = users.id AND profile_dislikes.viewer_id = $my_user->id
    where users.id <> $my_user->id
    having profile_likes.profile_id is null and profile_dislikes.profile_id is null





    share|improve this answer





















      Your Answer




      StackExchange.ifUsing("editor", function ()
      return StackExchange.using("mathjaxEditing", function ()
      StackExchange.MarkdownEditor.creationCallbacks.add(function (editor, postfix)
      StackExchange.mathjaxEditing.prepareWmdForMathJax(editor, postfix, [["\$", "\$"]]);
      );
      );
      , "mathjax-editing");

      StackExchange.ifUsing("editor", function ()
      StackExchange.using("externalEditor", function ()
      StackExchange.using("snippets", function ()
      StackExchange.snippets.init();
      );
      );
      , "code-snippets");

      StackExchange.ready(function()
      var channelOptions =
      tags: "".split(" "),
      id: "196"
      ;
      initTagRenderer("".split(" "), "".split(" "), channelOptions);

      StackExchange.using("externalEditor", function()
      // Have to fire editor after snippets, if snippets enabled
      if (StackExchange.settings.snippets.snippetsEnabled)
      StackExchange.using("snippets", function()
      createEditor();
      );

      else
      createEditor();

      );

      function createEditor()
      StackExchange.prepareEditor(
      heartbeatType: 'answer',
      convertImagesToLinks: false,
      noModals: false,
      showLowRepImageUploadWarning: true,
      reputationToPostImages: null,
      bindNavPrevention: true,
      postfix: "",
      onDemand: true,
      discardSelector: ".discard-answer"
      ,immediatelyShowMarkdownHelp:true
      );



      );








       

      draft saved


      draft discarded


















      StackExchange.ready(
      function ()
      StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f187293%2fsql-to-find-users-that-the-current-user-hasnt-liked-disliked%23new-answer', 'question_page');

      );

      Post as a guest






























      2 Answers
      2






      active

      oldest

      votes








      2 Answers
      2






      active

      oldest

      votes









      active

      oldest

      votes






      active

      oldest

      votes








      up vote
      2
      down vote



      accepted










      Do you really need to keep likes and dislikes in different tables?



      Seems to me that these would be pretty much the same thing with a different value in one single field. So maybe a profile_reactions table instead. Doing this would simplify your data model down to two tables and probably be more scalable in the future, if/when you want to ad more reaction types.



      SELECT * should typically be avoided. It makes it harder for readers of the code to understand what fields they have to work with in the result set (they would have to go look at database schema to understand this). It can be more bandwidth intensive than necessary. It you only want a few fields from a table with a large number of fields, why pull all the extra data? It can potentially make the code more fragile. Can a database schema change potentially introduce bugs in code?



      Consider whether relational storage (i.e. MySQL) is right for this in the long term. Since you are going to be dealing with most of the records in these tables, this is a query that really could be problematic over time, getting slower and slower as your user base grows. It simply doesn't scale well. The primary question to ask is whether the likes/dislikes (reactions) have any meaning outside the context of an owning user. My guess is that they don't. And if they don't, why does this data need to be normalized into it's own table?



      This data model is one that actually works quite well in NoSQL storage, where the data model might look like this:



      // User

      "id": 123,
      "reactions_to_users":
      234: "like",
      456: "dislike",
      789: "like",
      ...

      "reactions_from_users":
      234: "like",
      456: "dislike",
      111: "like",
      ,
      ...



      So assuming that you would already have this user object loaded up earlier in code (like you do know). When you go to make your query against the data store to get the the un-liked/disliked users, you would simply need to get the user ids under reactions_to_users and make a query against the data store for all users outside of this set.



      I think your actual SQL code in PHP looks good. I prefer using heredoc or nowdoc syntax like you are doing. I also prefer SQL the read more vertically than horizontally, breaking up at each SQL phrase like you have done. I wouldn't change your style here.






      share|improve this answer

























        up vote
        2
        down vote



        accepted










        Do you really need to keep likes and dislikes in different tables?



        Seems to me that these would be pretty much the same thing with a different value in one single field. So maybe a profile_reactions table instead. Doing this would simplify your data model down to two tables and probably be more scalable in the future, if/when you want to ad more reaction types.



        SELECT * should typically be avoided. It makes it harder for readers of the code to understand what fields they have to work with in the result set (they would have to go look at database schema to understand this). It can be more bandwidth intensive than necessary. It you only want a few fields from a table with a large number of fields, why pull all the extra data? It can potentially make the code more fragile. Can a database schema change potentially introduce bugs in code?



        Consider whether relational storage (i.e. MySQL) is right for this in the long term. Since you are going to be dealing with most of the records in these tables, this is a query that really could be problematic over time, getting slower and slower as your user base grows. It simply doesn't scale well. The primary question to ask is whether the likes/dislikes (reactions) have any meaning outside the context of an owning user. My guess is that they don't. And if they don't, why does this data need to be normalized into it's own table?



        This data model is one that actually works quite well in NoSQL storage, where the data model might look like this:



        // User

        "id": 123,
        "reactions_to_users":
        234: "like",
        456: "dislike",
        789: "like",
        ...

        "reactions_from_users":
        234: "like",
        456: "dislike",
        111: "like",
        ,
        ...



        So assuming that you would already have this user object loaded up earlier in code (like you do know). When you go to make your query against the data store to get the the un-liked/disliked users, you would simply need to get the user ids under reactions_to_users and make a query against the data store for all users outside of this set.



        I think your actual SQL code in PHP looks good. I prefer using heredoc or nowdoc syntax like you are doing. I also prefer SQL the read more vertically than horizontally, breaking up at each SQL phrase like you have done. I wouldn't change your style here.






        share|improve this answer























          up vote
          2
          down vote



          accepted







          up vote
          2
          down vote



          accepted






          Do you really need to keep likes and dislikes in different tables?



          Seems to me that these would be pretty much the same thing with a different value in one single field. So maybe a profile_reactions table instead. Doing this would simplify your data model down to two tables and probably be more scalable in the future, if/when you want to ad more reaction types.



          SELECT * should typically be avoided. It makes it harder for readers of the code to understand what fields they have to work with in the result set (they would have to go look at database schema to understand this). It can be more bandwidth intensive than necessary. It you only want a few fields from a table with a large number of fields, why pull all the extra data? It can potentially make the code more fragile. Can a database schema change potentially introduce bugs in code?



          Consider whether relational storage (i.e. MySQL) is right for this in the long term. Since you are going to be dealing with most of the records in these tables, this is a query that really could be problematic over time, getting slower and slower as your user base grows. It simply doesn't scale well. The primary question to ask is whether the likes/dislikes (reactions) have any meaning outside the context of an owning user. My guess is that they don't. And if they don't, why does this data need to be normalized into it's own table?



          This data model is one that actually works quite well in NoSQL storage, where the data model might look like this:



          // User

          "id": 123,
          "reactions_to_users":
          234: "like",
          456: "dislike",
          789: "like",
          ...

          "reactions_from_users":
          234: "like",
          456: "dislike",
          111: "like",
          ,
          ...



          So assuming that you would already have this user object loaded up earlier in code (like you do know). When you go to make your query against the data store to get the the un-liked/disliked users, you would simply need to get the user ids under reactions_to_users and make a query against the data store for all users outside of this set.



          I think your actual SQL code in PHP looks good. I prefer using heredoc or nowdoc syntax like you are doing. I also prefer SQL the read more vertically than horizontally, breaking up at each SQL phrase like you have done. I wouldn't change your style here.






          share|improve this answer













          Do you really need to keep likes and dislikes in different tables?



          Seems to me that these would be pretty much the same thing with a different value in one single field. So maybe a profile_reactions table instead. Doing this would simplify your data model down to two tables and probably be more scalable in the future, if/when you want to ad more reaction types.



          SELECT * should typically be avoided. It makes it harder for readers of the code to understand what fields they have to work with in the result set (they would have to go look at database schema to understand this). It can be more bandwidth intensive than necessary. It you only want a few fields from a table with a large number of fields, why pull all the extra data? It can potentially make the code more fragile. Can a database schema change potentially introduce bugs in code?



          Consider whether relational storage (i.e. MySQL) is right for this in the long term. Since you are going to be dealing with most of the records in these tables, this is a query that really could be problematic over time, getting slower and slower as your user base grows. It simply doesn't scale well. The primary question to ask is whether the likes/dislikes (reactions) have any meaning outside the context of an owning user. My guess is that they don't. And if they don't, why does this data need to be normalized into it's own table?



          This data model is one that actually works quite well in NoSQL storage, where the data model might look like this:



          // User

          "id": 123,
          "reactions_to_users":
          234: "like",
          456: "dislike",
          789: "like",
          ...

          "reactions_from_users":
          234: "like",
          456: "dislike",
          111: "like",
          ,
          ...



          So assuming that you would already have this user object loaded up earlier in code (like you do know). When you go to make your query against the data store to get the the un-liked/disliked users, you would simply need to get the user ids under reactions_to_users and make a query against the data store for all users outside of this set.



          I think your actual SQL code in PHP looks good. I prefer using heredoc or nowdoc syntax like you are doing. I also prefer SQL the read more vertically than horizontally, breaking up at each SQL phrase like you have done. I wouldn't change your style here.







          share|improve this answer













          share|improve this answer



          share|improve this answer











          answered Feb 11 at 17:26









          Mike Brant

          8,663619




          8,663619






















              up vote
              3
              down vote













              Personally, I would just stick with what you have already got.



              You could try something like this, although too me it doesn't look any tidier.
              You would need to profile it against your database, to see if it performs any better.



              SELECT * 
              FROM users
              left outer join profile_likes on profile_likes.profile_id = users.id AND profile_likes.viewer_id = $my_user->id
              left outer join profile_dislikes on profile_dislikes.profile_id = users.id AND profile_dislikes.viewer_id = $my_user->id
              where users.id <> $my_user->id
              having profile_likes.profile_id is null and profile_dislikes.profile_id is null





              share|improve this answer

























                up vote
                3
                down vote













                Personally, I would just stick with what you have already got.



                You could try something like this, although too me it doesn't look any tidier.
                You would need to profile it against your database, to see if it performs any better.



                SELECT * 
                FROM users
                left outer join profile_likes on profile_likes.profile_id = users.id AND profile_likes.viewer_id = $my_user->id
                left outer join profile_dislikes on profile_dislikes.profile_id = users.id AND profile_dislikes.viewer_id = $my_user->id
                where users.id <> $my_user->id
                having profile_likes.profile_id is null and profile_dislikes.profile_id is null





                share|improve this answer























                  up vote
                  3
                  down vote










                  up vote
                  3
                  down vote









                  Personally, I would just stick with what you have already got.



                  You could try something like this, although too me it doesn't look any tidier.
                  You would need to profile it against your database, to see if it performs any better.



                  SELECT * 
                  FROM users
                  left outer join profile_likes on profile_likes.profile_id = users.id AND profile_likes.viewer_id = $my_user->id
                  left outer join profile_dislikes on profile_dislikes.profile_id = users.id AND profile_dislikes.viewer_id = $my_user->id
                  where users.id <> $my_user->id
                  having profile_likes.profile_id is null and profile_dislikes.profile_id is null





                  share|improve this answer













                  Personally, I would just stick with what you have already got.



                  You could try something like this, although too me it doesn't look any tidier.
                  You would need to profile it against your database, to see if it performs any better.



                  SELECT * 
                  FROM users
                  left outer join profile_likes on profile_likes.profile_id = users.id AND profile_likes.viewer_id = $my_user->id
                  left outer join profile_dislikes on profile_dislikes.profile_id = users.id AND profile_dislikes.viewer_id = $my_user->id
                  where users.id <> $my_user->id
                  having profile_likes.profile_id is null and profile_dislikes.profile_id is null






                  share|improve this answer













                  share|improve this answer



                  share|improve this answer











                  answered Feb 11 at 6:19









                  bumperbox

                  1,800614




                  1,800614






















                       

                      draft saved


                      draft discarded


























                       


                      draft saved


                      draft discarded














                      StackExchange.ready(
                      function ()
                      StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f187293%2fsql-to-find-users-that-the-current-user-hasnt-liked-disliked%23new-answer', 'question_page');

                      );

                      Post as a guest













































































                      Popular posts from this blog

                      Python Lists

                      Aion

                      JavaScript Array Iteration Methods