UNION query ordered separately by its components












1















I'm using Postgres 9.6 and I have a query that shows the top 100 rows that contain a specific string, "xyz" in this example. It looks roughly like the following:



SELECT name FROM data WHERE name ILIKE '%xyz%' ORDER BY other_column LIMIT 100;


What I actually want to achieve is to put the rows that match at the beginning of the name column in front. So still 100 rows in total, but first filled from results that match name ILIKE 'xyz%, and then with results from ILIKE '%xyz%'.



I tried to achieve this with UNION queries roughly like the following



SELECT name FROM data WHERE name ILIKE 'xyz%' 
UNION
SELECT name FROM data WHERE name ILIKE '%xyz%'
ORDER BY other_column
LIMIT 100;


This obviously doesn't work because it's sorted by other_column. My original idea was to add a column to each query to use for sorting



SELECT 1 as sort_order, name FROM data WHERE name ILIKE 'xyz%' 
UNION
SELECT 2 as sort_order, name FROM data WHERE name ILIKE '%xyz%'
ORDER BY sort_order, other_column
LIMIT 100;


But that kills the duplicate removal of UNION and instead behaves like UNION ALL because I'm making originally identical rows different with the addition of the sort_order column.



I could of course do the duplicate removal in a later step outside the database, but that isn't a terribly appealing solution to me. Is there any way to order the individual parts of a UNION query separately, and achieve the results I described?










share|improve this question



























    1















    I'm using Postgres 9.6 and I have a query that shows the top 100 rows that contain a specific string, "xyz" in this example. It looks roughly like the following:



    SELECT name FROM data WHERE name ILIKE '%xyz%' ORDER BY other_column LIMIT 100;


    What I actually want to achieve is to put the rows that match at the beginning of the name column in front. So still 100 rows in total, but first filled from results that match name ILIKE 'xyz%, and then with results from ILIKE '%xyz%'.



    I tried to achieve this with UNION queries roughly like the following



    SELECT name FROM data WHERE name ILIKE 'xyz%' 
    UNION
    SELECT name FROM data WHERE name ILIKE '%xyz%'
    ORDER BY other_column
    LIMIT 100;


    This obviously doesn't work because it's sorted by other_column. My original idea was to add a column to each query to use for sorting



    SELECT 1 as sort_order, name FROM data WHERE name ILIKE 'xyz%' 
    UNION
    SELECT 2 as sort_order, name FROM data WHERE name ILIKE '%xyz%'
    ORDER BY sort_order, other_column
    LIMIT 100;


    But that kills the duplicate removal of UNION and instead behaves like UNION ALL because I'm making originally identical rows different with the addition of the sort_order column.



    I could of course do the duplicate removal in a later step outside the database, but that isn't a terribly appealing solution to me. Is there any way to order the individual parts of a UNION query separately, and achieve the results I described?










    share|improve this question

























      1












      1








      1








      I'm using Postgres 9.6 and I have a query that shows the top 100 rows that contain a specific string, "xyz" in this example. It looks roughly like the following:



      SELECT name FROM data WHERE name ILIKE '%xyz%' ORDER BY other_column LIMIT 100;


      What I actually want to achieve is to put the rows that match at the beginning of the name column in front. So still 100 rows in total, but first filled from results that match name ILIKE 'xyz%, and then with results from ILIKE '%xyz%'.



      I tried to achieve this with UNION queries roughly like the following



      SELECT name FROM data WHERE name ILIKE 'xyz%' 
      UNION
      SELECT name FROM data WHERE name ILIKE '%xyz%'
      ORDER BY other_column
      LIMIT 100;


      This obviously doesn't work because it's sorted by other_column. My original idea was to add a column to each query to use for sorting



      SELECT 1 as sort_order, name FROM data WHERE name ILIKE 'xyz%' 
      UNION
      SELECT 2 as sort_order, name FROM data WHERE name ILIKE '%xyz%'
      ORDER BY sort_order, other_column
      LIMIT 100;


      But that kills the duplicate removal of UNION and instead behaves like UNION ALL because I'm making originally identical rows different with the addition of the sort_order column.



      I could of course do the duplicate removal in a later step outside the database, but that isn't a terribly appealing solution to me. Is there any way to order the individual parts of a UNION query separately, and achieve the results I described?










      share|improve this question














      I'm using Postgres 9.6 and I have a query that shows the top 100 rows that contain a specific string, "xyz" in this example. It looks roughly like the following:



      SELECT name FROM data WHERE name ILIKE '%xyz%' ORDER BY other_column LIMIT 100;


      What I actually want to achieve is to put the rows that match at the beginning of the name column in front. So still 100 rows in total, but first filled from results that match name ILIKE 'xyz%, and then with results from ILIKE '%xyz%'.



      I tried to achieve this with UNION queries roughly like the following



      SELECT name FROM data WHERE name ILIKE 'xyz%' 
      UNION
      SELECT name FROM data WHERE name ILIKE '%xyz%'
      ORDER BY other_column
      LIMIT 100;


      This obviously doesn't work because it's sorted by other_column. My original idea was to add a column to each query to use for sorting



      SELECT 1 as sort_order, name FROM data WHERE name ILIKE 'xyz%' 
      UNION
      SELECT 2 as sort_order, name FROM data WHERE name ILIKE '%xyz%'
      ORDER BY sort_order, other_column
      LIMIT 100;


      But that kills the duplicate removal of UNION and instead behaves like UNION ALL because I'm making originally identical rows different with the addition of the sort_order column.



      I could of course do the duplicate removal in a later step outside the database, but that isn't a terribly appealing solution to me. Is there any way to order the individual parts of a UNION query separately, and achieve the results I described?







      sql postgresql union






      share|improve this question













      share|improve this question











      share|improve this question




      share|improve this question










      asked Nov 21 '18 at 17:23









      Mad ScientistMad Scientist

      11.2k96286




      11.2k96286
























          2 Answers
          2






          active

          oldest

          votes


















          1














          Use the function position(substring in string) instead of UNION, example:



          WITH data(name, other_column) AS (
          VALUES
          ('abc xyz', 1),
          ('xyz abc', 2),
          ('a xyz b', 3),
          ('xyz abc', 4)
          )

          SELECT name, other_column
          FROM data
          WHERE name ILIKE '%xyz%'
          ORDER BY position('xyz' in name), other_column
          LIMIT 100;

          name | other_column
          ---------+--------------
          xyz abc | 2
          xyz abc | 4
          a xyz b | 3
          abc xyz | 1
          (4 rows)


          or:



          SELECT name, other_column
          FROM data
          WHERE name ILIKE '%xyz%'
          ORDER BY position('xyz' in name) > 1, other_column
          LIMIT 100;

          name | other_column
          ---------+--------------
          xyz abc | 2
          xyz abc | 4
          abc xyz | 1
          a xyz b | 3
          (4 rows)





          share|improve this answer


























          • So zxyz will be before aaxyz

            – forpas
            Nov 21 '18 at 18:01











          • @forpas - Use the second variant, if it matters.

            – klin
            Nov 21 '18 at 18:03



















          0














          By using other_column:



          SELECT t.name FROM (
          SELECT name, CONCAT(' ', name) AS other_column FROM data WHERE (name ILIKE 'xyz%')
          UNION
          SELECT name, name AS other_column FROM data WHERE (name ILIKE '%xyz%') AND (name NOT ILIKE 'xyz%')
          ) AS t
          ORDER BY t.other_column
          LIMIT 100;


          What this does is append a prefix of blank space (ASCII 32) to name in the first list and creates other_column.

          In the second list other_column is the same as the name.

          Finally sorts by other_column.






          share|improve this answer


























          • as far as I understand, the sort order is unpredictable in case of UNION without an ORDER BY clause due to the duplicate removal.

            – Mad Scientist
            Nov 21 '18 at 17:43











          • so this will make me think about it. I will delete after you read.

            – forpas
            Nov 21 '18 at 17:46











          • @MadScientist check this.

            – forpas
            Nov 21 '18 at 18:12











          Your Answer






          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: "1"
          };
          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',
          autoActivateHeartbeat: false,
          convertImagesToLinks: true,
          noModals: true,
          showLowRepImageUploadWarning: true,
          reputationToPostImages: 10,
          bindNavPrevention: true,
          postfix: "",
          imageUploader: {
          brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
          contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
          allowUrls: true
          },
          onDemand: true,
          discardSelector: ".discard-answer"
          ,immediatelyShowMarkdownHelp:true
          });


          }
          });














          draft saved

          draft discarded


















          StackExchange.ready(
          function () {
          StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53417540%2funion-query-ordered-separately-by-its-components%23new-answer', 'question_page');
          }
          );

          Post as a guest















          Required, but never shown

























          2 Answers
          2






          active

          oldest

          votes








          2 Answers
          2






          active

          oldest

          votes









          active

          oldest

          votes






          active

          oldest

          votes









          1














          Use the function position(substring in string) instead of UNION, example:



          WITH data(name, other_column) AS (
          VALUES
          ('abc xyz', 1),
          ('xyz abc', 2),
          ('a xyz b', 3),
          ('xyz abc', 4)
          )

          SELECT name, other_column
          FROM data
          WHERE name ILIKE '%xyz%'
          ORDER BY position('xyz' in name), other_column
          LIMIT 100;

          name | other_column
          ---------+--------------
          xyz abc | 2
          xyz abc | 4
          a xyz b | 3
          abc xyz | 1
          (4 rows)


          or:



          SELECT name, other_column
          FROM data
          WHERE name ILIKE '%xyz%'
          ORDER BY position('xyz' in name) > 1, other_column
          LIMIT 100;

          name | other_column
          ---------+--------------
          xyz abc | 2
          xyz abc | 4
          abc xyz | 1
          a xyz b | 3
          (4 rows)





          share|improve this answer


























          • So zxyz will be before aaxyz

            – forpas
            Nov 21 '18 at 18:01











          • @forpas - Use the second variant, if it matters.

            – klin
            Nov 21 '18 at 18:03
















          1














          Use the function position(substring in string) instead of UNION, example:



          WITH data(name, other_column) AS (
          VALUES
          ('abc xyz', 1),
          ('xyz abc', 2),
          ('a xyz b', 3),
          ('xyz abc', 4)
          )

          SELECT name, other_column
          FROM data
          WHERE name ILIKE '%xyz%'
          ORDER BY position('xyz' in name), other_column
          LIMIT 100;

          name | other_column
          ---------+--------------
          xyz abc | 2
          xyz abc | 4
          a xyz b | 3
          abc xyz | 1
          (4 rows)


          or:



          SELECT name, other_column
          FROM data
          WHERE name ILIKE '%xyz%'
          ORDER BY position('xyz' in name) > 1, other_column
          LIMIT 100;

          name | other_column
          ---------+--------------
          xyz abc | 2
          xyz abc | 4
          abc xyz | 1
          a xyz b | 3
          (4 rows)





          share|improve this answer


























          • So zxyz will be before aaxyz

            – forpas
            Nov 21 '18 at 18:01











          • @forpas - Use the second variant, if it matters.

            – klin
            Nov 21 '18 at 18:03














          1












          1








          1







          Use the function position(substring in string) instead of UNION, example:



          WITH data(name, other_column) AS (
          VALUES
          ('abc xyz', 1),
          ('xyz abc', 2),
          ('a xyz b', 3),
          ('xyz abc', 4)
          )

          SELECT name, other_column
          FROM data
          WHERE name ILIKE '%xyz%'
          ORDER BY position('xyz' in name), other_column
          LIMIT 100;

          name | other_column
          ---------+--------------
          xyz abc | 2
          xyz abc | 4
          a xyz b | 3
          abc xyz | 1
          (4 rows)


          or:



          SELECT name, other_column
          FROM data
          WHERE name ILIKE '%xyz%'
          ORDER BY position('xyz' in name) > 1, other_column
          LIMIT 100;

          name | other_column
          ---------+--------------
          xyz abc | 2
          xyz abc | 4
          abc xyz | 1
          a xyz b | 3
          (4 rows)





          share|improve this answer















          Use the function position(substring in string) instead of UNION, example:



          WITH data(name, other_column) AS (
          VALUES
          ('abc xyz', 1),
          ('xyz abc', 2),
          ('a xyz b', 3),
          ('xyz abc', 4)
          )

          SELECT name, other_column
          FROM data
          WHERE name ILIKE '%xyz%'
          ORDER BY position('xyz' in name), other_column
          LIMIT 100;

          name | other_column
          ---------+--------------
          xyz abc | 2
          xyz abc | 4
          a xyz b | 3
          abc xyz | 1
          (4 rows)


          or:



          SELECT name, other_column
          FROM data
          WHERE name ILIKE '%xyz%'
          ORDER BY position('xyz' in name) > 1, other_column
          LIMIT 100;

          name | other_column
          ---------+--------------
          xyz abc | 2
          xyz abc | 4
          abc xyz | 1
          a xyz b | 3
          (4 rows)






          share|improve this answer














          share|improve this answer



          share|improve this answer








          edited Nov 21 '18 at 18:02

























          answered Nov 21 '18 at 17:57









          klinklin

          58.6k65182




          58.6k65182













          • So zxyz will be before aaxyz

            – forpas
            Nov 21 '18 at 18:01











          • @forpas - Use the second variant, if it matters.

            – klin
            Nov 21 '18 at 18:03



















          • So zxyz will be before aaxyz

            – forpas
            Nov 21 '18 at 18:01











          • @forpas - Use the second variant, if it matters.

            – klin
            Nov 21 '18 at 18:03

















          So zxyz will be before aaxyz

          – forpas
          Nov 21 '18 at 18:01





          So zxyz will be before aaxyz

          – forpas
          Nov 21 '18 at 18:01













          @forpas - Use the second variant, if it matters.

          – klin
          Nov 21 '18 at 18:03





          @forpas - Use the second variant, if it matters.

          – klin
          Nov 21 '18 at 18:03













          0














          By using other_column:



          SELECT t.name FROM (
          SELECT name, CONCAT(' ', name) AS other_column FROM data WHERE (name ILIKE 'xyz%')
          UNION
          SELECT name, name AS other_column FROM data WHERE (name ILIKE '%xyz%') AND (name NOT ILIKE 'xyz%')
          ) AS t
          ORDER BY t.other_column
          LIMIT 100;


          What this does is append a prefix of blank space (ASCII 32) to name in the first list and creates other_column.

          In the second list other_column is the same as the name.

          Finally sorts by other_column.






          share|improve this answer


























          • as far as I understand, the sort order is unpredictable in case of UNION without an ORDER BY clause due to the duplicate removal.

            – Mad Scientist
            Nov 21 '18 at 17:43











          • so this will make me think about it. I will delete after you read.

            – forpas
            Nov 21 '18 at 17:46











          • @MadScientist check this.

            – forpas
            Nov 21 '18 at 18:12
















          0














          By using other_column:



          SELECT t.name FROM (
          SELECT name, CONCAT(' ', name) AS other_column FROM data WHERE (name ILIKE 'xyz%')
          UNION
          SELECT name, name AS other_column FROM data WHERE (name ILIKE '%xyz%') AND (name NOT ILIKE 'xyz%')
          ) AS t
          ORDER BY t.other_column
          LIMIT 100;


          What this does is append a prefix of blank space (ASCII 32) to name in the first list and creates other_column.

          In the second list other_column is the same as the name.

          Finally sorts by other_column.






          share|improve this answer


























          • as far as I understand, the sort order is unpredictable in case of UNION without an ORDER BY clause due to the duplicate removal.

            – Mad Scientist
            Nov 21 '18 at 17:43











          • so this will make me think about it. I will delete after you read.

            – forpas
            Nov 21 '18 at 17:46











          • @MadScientist check this.

            – forpas
            Nov 21 '18 at 18:12














          0












          0








          0







          By using other_column:



          SELECT t.name FROM (
          SELECT name, CONCAT(' ', name) AS other_column FROM data WHERE (name ILIKE 'xyz%')
          UNION
          SELECT name, name AS other_column FROM data WHERE (name ILIKE '%xyz%') AND (name NOT ILIKE 'xyz%')
          ) AS t
          ORDER BY t.other_column
          LIMIT 100;


          What this does is append a prefix of blank space (ASCII 32) to name in the first list and creates other_column.

          In the second list other_column is the same as the name.

          Finally sorts by other_column.






          share|improve this answer















          By using other_column:



          SELECT t.name FROM (
          SELECT name, CONCAT(' ', name) AS other_column FROM data WHERE (name ILIKE 'xyz%')
          UNION
          SELECT name, name AS other_column FROM data WHERE (name ILIKE '%xyz%') AND (name NOT ILIKE 'xyz%')
          ) AS t
          ORDER BY t.other_column
          LIMIT 100;


          What this does is append a prefix of blank space (ASCII 32) to name in the first list and creates other_column.

          In the second list other_column is the same as the name.

          Finally sorts by other_column.







          share|improve this answer














          share|improve this answer



          share|improve this answer








          edited Nov 21 '18 at 18:40

























          answered Nov 21 '18 at 17:40









          forpasforpas

          12.5k3424




          12.5k3424













          • as far as I understand, the sort order is unpredictable in case of UNION without an ORDER BY clause due to the duplicate removal.

            – Mad Scientist
            Nov 21 '18 at 17:43











          • so this will make me think about it. I will delete after you read.

            – forpas
            Nov 21 '18 at 17:46











          • @MadScientist check this.

            – forpas
            Nov 21 '18 at 18:12



















          • as far as I understand, the sort order is unpredictable in case of UNION without an ORDER BY clause due to the duplicate removal.

            – Mad Scientist
            Nov 21 '18 at 17:43











          • so this will make me think about it. I will delete after you read.

            – forpas
            Nov 21 '18 at 17:46











          • @MadScientist check this.

            – forpas
            Nov 21 '18 at 18:12

















          as far as I understand, the sort order is unpredictable in case of UNION without an ORDER BY clause due to the duplicate removal.

          – Mad Scientist
          Nov 21 '18 at 17:43





          as far as I understand, the sort order is unpredictable in case of UNION without an ORDER BY clause due to the duplicate removal.

          – Mad Scientist
          Nov 21 '18 at 17:43













          so this will make me think about it. I will delete after you read.

          – forpas
          Nov 21 '18 at 17:46





          so this will make me think about it. I will delete after you read.

          – forpas
          Nov 21 '18 at 17:46













          @MadScientist check this.

          – forpas
          Nov 21 '18 at 18:12





          @MadScientist check this.

          – forpas
          Nov 21 '18 at 18:12


















          draft saved

          draft discarded




















































          Thanks for contributing an answer to Stack Overflow!


          • Please be sure to answer the question. Provide details and share your research!

          But avoid



          • Asking for help, clarification, or responding to other answers.

          • Making statements based on opinion; back them up with references or personal experience.


          To learn more, see our tips on writing great answers.




          draft saved


          draft discarded














          StackExchange.ready(
          function () {
          StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53417540%2funion-query-ordered-separately-by-its-components%23new-answer', 'question_page');
          }
          );

          Post as a guest















          Required, but never shown





















































          Required, but never shown














          Required, but never shown












          Required, but never shown







          Required, but never shown

































          Required, but never shown














          Required, but never shown












          Required, but never shown







          Required, but never shown







          Popular posts from this blog

          "Incorrect syntax near the keyword 'ON'. (on update cascade, on delete cascade,)

          Alcedinidae

          Origin of the phrase “under your belt”?