Join three rows if the same value in one column












0















There is a Postgres database and the table has three columns. The data structure is in external system so I can not modify it.



Every object is represented by three rows (identified by column element_id - rows with the same value in this column represents the same object), for example:



key     value            element_id
-----------------------------------
status active 1
name exampleNameAAA 1
city exampleCityAAA 1
status inactive 2
name exampleNameBBB 2
city exampleCityBBB 2
status inactive 3
name exampleNameCCC 3
city exampleCityCCC 3


I want to get all values describing every objects (name, status and city).



For this example the output should be like:



exampleNameAAA   | active    | exampleCityAAA
exampleNameBBB | inactive | exampleCityBBB
exampleNameCCC | inactive | exampleCityCCC


I know how to join two rows:



select a.value as name,
b.value as status
from the_table a
join the_table b
on a.element_id = b.element_id
and b."key" = 'status'
where a."key" = 'name';


How is it possible to join three columns?










share|improve this question




















  • 1





    Simply add another JOIN. Keep the one and only WHERE at the end.

    – jarlh
    Nov 23 '18 at 10:06













  • @jarlh could you post the example?

    – john bernat
    Nov 23 '18 at 10:07











  • SELECT t1.c1, t2.cx ... FROM t1 JOIN t2 ON ... JOIN t3 ON ... WHERE...

    – jarlh
    Nov 23 '18 at 10:09
















0















There is a Postgres database and the table has three columns. The data structure is in external system so I can not modify it.



Every object is represented by three rows (identified by column element_id - rows with the same value in this column represents the same object), for example:



key     value            element_id
-----------------------------------
status active 1
name exampleNameAAA 1
city exampleCityAAA 1
status inactive 2
name exampleNameBBB 2
city exampleCityBBB 2
status inactive 3
name exampleNameCCC 3
city exampleCityCCC 3


I want to get all values describing every objects (name, status and city).



For this example the output should be like:



exampleNameAAA   | active    | exampleCityAAA
exampleNameBBB | inactive | exampleCityBBB
exampleNameCCC | inactive | exampleCityCCC


I know how to join two rows:



select a.value as name,
b.value as status
from the_table a
join the_table b
on a.element_id = b.element_id
and b."key" = 'status'
where a."key" = 'name';


How is it possible to join three columns?










share|improve this question




















  • 1





    Simply add another JOIN. Keep the one and only WHERE at the end.

    – jarlh
    Nov 23 '18 at 10:06













  • @jarlh could you post the example?

    – john bernat
    Nov 23 '18 at 10:07











  • SELECT t1.c1, t2.cx ... FROM t1 JOIN t2 ON ... JOIN t3 ON ... WHERE...

    – jarlh
    Nov 23 '18 at 10:09














0












0








0








There is a Postgres database and the table has three columns. The data structure is in external system so I can not modify it.



Every object is represented by three rows (identified by column element_id - rows with the same value in this column represents the same object), for example:



key     value            element_id
-----------------------------------
status active 1
name exampleNameAAA 1
city exampleCityAAA 1
status inactive 2
name exampleNameBBB 2
city exampleCityBBB 2
status inactive 3
name exampleNameCCC 3
city exampleCityCCC 3


I want to get all values describing every objects (name, status and city).



For this example the output should be like:



exampleNameAAA   | active    | exampleCityAAA
exampleNameBBB | inactive | exampleCityBBB
exampleNameCCC | inactive | exampleCityCCC


I know how to join two rows:



select a.value as name,
b.value as status
from the_table a
join the_table b
on a.element_id = b.element_id
and b."key" = 'status'
where a."key" = 'name';


How is it possible to join three columns?










share|improve this question
















There is a Postgres database and the table has three columns. The data structure is in external system so I can not modify it.



Every object is represented by three rows (identified by column element_id - rows with the same value in this column represents the same object), for example:



key     value            element_id
-----------------------------------
status active 1
name exampleNameAAA 1
city exampleCityAAA 1
status inactive 2
name exampleNameBBB 2
city exampleCityBBB 2
status inactive 3
name exampleNameCCC 3
city exampleCityCCC 3


I want to get all values describing every objects (name, status and city).



For this example the output should be like:



exampleNameAAA   | active    | exampleCityAAA
exampleNameBBB | inactive | exampleCityBBB
exampleNameCCC | inactive | exampleCityCCC


I know how to join two rows:



select a.value as name,
b.value as status
from the_table a
join the_table b
on a.element_id = b.element_id
and b."key" = 'status'
where a."key" = 'name';


How is it possible to join three columns?







sql database postgresql






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited Nov 23 '18 at 10:01









Mayank Porwal

5,0182725




5,0182725










asked Nov 23 '18 at 9:59









john bernatjohn bernat

174




174








  • 1





    Simply add another JOIN. Keep the one and only WHERE at the end.

    – jarlh
    Nov 23 '18 at 10:06













  • @jarlh could you post the example?

    – john bernat
    Nov 23 '18 at 10:07











  • SELECT t1.c1, t2.cx ... FROM t1 JOIN t2 ON ... JOIN t3 ON ... WHERE...

    – jarlh
    Nov 23 '18 at 10:09














  • 1





    Simply add another JOIN. Keep the one and only WHERE at the end.

    – jarlh
    Nov 23 '18 at 10:06













  • @jarlh could you post the example?

    – john bernat
    Nov 23 '18 at 10:07











  • SELECT t1.c1, t2.cx ... FROM t1 JOIN t2 ON ... JOIN t3 ON ... WHERE...

    – jarlh
    Nov 23 '18 at 10:09








1




1





Simply add another JOIN. Keep the one and only WHERE at the end.

– jarlh
Nov 23 '18 at 10:06







Simply add another JOIN. Keep the one and only WHERE at the end.

– jarlh
Nov 23 '18 at 10:06















@jarlh could you post the example?

– john bernat
Nov 23 '18 at 10:07





@jarlh could you post the example?

– john bernat
Nov 23 '18 at 10:07













SELECT t1.c1, t2.cx ... FROM t1 JOIN t2 ON ... JOIN t3 ON ... WHERE...

– jarlh
Nov 23 '18 at 10:09





SELECT t1.c1, t2.cx ... FROM t1 JOIN t2 ON ... JOIN t3 ON ... WHERE...

– jarlh
Nov 23 '18 at 10:09












3 Answers
3






active

oldest

votes


















0














You can try below



DEMO



select a.value as name,
b.value as status,c.value as city
from t1 a
join t1 b
on a.element_id = b.element_id and b."keys" = 'status'
join t1 c on a.element_id = c.element_id and c."keys" = 'city'
where a."keys" = 'name';


OUTPUT



name            status      city
exampleNameAAA active exampleCityAAA
exampleNameBBB inactive exampleCityBBB
exampleNameCCC inactive exampleCityCCC





share|improve this answer































    0














    One option is to simply add another join for each value you need (this is one of the big disadvantages of the EAV (anti) pattern you are using:



    select a.value as name,
    b.value as status,
    c.value as city
    from the_table a
    join the_table b on a.element_id = b.element_id and b."key" = 'status'
    join the_table c on a.element_id = c.element_id and c."key" = 'city'
    where a."key" = 'name';




    Another option is to aggregate all key/value pairs for an element into a JSON then you can easily access each one without additional joins:



    select t.element_id, 
    t.obj ->> 'city' as city,
    t.obj ->> 'status' as status,
    t.obj ->> 'name' as name
    from (
    select e.element_id, jsonb_object_agg("key", value) as obj
    from element e
    group by e.element_id
    ) t;


    If the table is really big this might be a lot slower than the join version due to the aggregation step. If you limit the query to only some elements (e.g. by adding a where element_id = 1 or where element_id in (1,2,3)) then this should be quite fast.



    It has the advantage that you always have all key/value pairs for each element_id available regardless on what you do. The inner select could be put into a view, to make things easier.



    Online example: https://rextester.com/MSZOWU37182






    share|improve this answer































      0














      Seems like you want to PIVOT



      One way to do that is via conditional aggregation.



      select 
      -- t.element_id,
      max(case when t.key = 'name' then t.value end) as name,
      max(case when t.key = 'status' then t.value end) as status,
      max(case when t.key = 'city' then t.value end) as city
      from the_table t
      group by t.element_id;


      db<>fiddle here



      Or use crosstab:



      select 
      -- element_id,
      name,
      status,
      city
      from crosstab (
      'select t.element_id, t.key, t.value
      from the_table t'
      ) as ct (element_id int, name varchar(30), status varchar(30), city varchar(30));


      But if you do like those joins, here's a way



      select 
      -- el.element_id,
      nm.value as name,
      st.value as status,
      ci.value as city
      from
      (
      select distinct t.element_id
      from the_table t
      where t.key in ('name','status','city')
      ) as el
      left join the_table as nm on (nm.element_id = el.element_id and nm.key = 'name')
      left join the_table as st on (st.element_id = el.element_id and st.key = 'status')
      left join the_table as ci on (ci.element_id = el.element_id and ci.key = 'city');







      share|improve this answer


























        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%2f53444419%2fjoin-three-rows-if-the-same-value-in-one-column%23new-answer', 'question_page');
        }
        );

        Post as a guest















        Required, but never shown

























        3 Answers
        3






        active

        oldest

        votes








        3 Answers
        3






        active

        oldest

        votes









        active

        oldest

        votes






        active

        oldest

        votes









        0














        You can try below



        DEMO



        select a.value as name,
        b.value as status,c.value as city
        from t1 a
        join t1 b
        on a.element_id = b.element_id and b."keys" = 'status'
        join t1 c on a.element_id = c.element_id and c."keys" = 'city'
        where a."keys" = 'name';


        OUTPUT



        name            status      city
        exampleNameAAA active exampleCityAAA
        exampleNameBBB inactive exampleCityBBB
        exampleNameCCC inactive exampleCityCCC





        share|improve this answer




























          0














          You can try below



          DEMO



          select a.value as name,
          b.value as status,c.value as city
          from t1 a
          join t1 b
          on a.element_id = b.element_id and b."keys" = 'status'
          join t1 c on a.element_id = c.element_id and c."keys" = 'city'
          where a."keys" = 'name';


          OUTPUT



          name            status      city
          exampleNameAAA active exampleCityAAA
          exampleNameBBB inactive exampleCityBBB
          exampleNameCCC inactive exampleCityCCC





          share|improve this answer


























            0












            0








            0







            You can try below



            DEMO



            select a.value as name,
            b.value as status,c.value as city
            from t1 a
            join t1 b
            on a.element_id = b.element_id and b."keys" = 'status'
            join t1 c on a.element_id = c.element_id and c."keys" = 'city'
            where a."keys" = 'name';


            OUTPUT



            name            status      city
            exampleNameAAA active exampleCityAAA
            exampleNameBBB inactive exampleCityBBB
            exampleNameCCC inactive exampleCityCCC





            share|improve this answer













            You can try below



            DEMO



            select a.value as name,
            b.value as status,c.value as city
            from t1 a
            join t1 b
            on a.element_id = b.element_id and b."keys" = 'status'
            join t1 c on a.element_id = c.element_id and c."keys" = 'city'
            where a."keys" = 'name';


            OUTPUT



            name            status      city
            exampleNameAAA active exampleCityAAA
            exampleNameBBB inactive exampleCityBBB
            exampleNameCCC inactive exampleCityCCC






            share|improve this answer












            share|improve this answer



            share|improve this answer










            answered Nov 23 '18 at 10:22









            fa06fa06

            18.5k21019




            18.5k21019

























                0














                One option is to simply add another join for each value you need (this is one of the big disadvantages of the EAV (anti) pattern you are using:



                select a.value as name,
                b.value as status,
                c.value as city
                from the_table a
                join the_table b on a.element_id = b.element_id and b."key" = 'status'
                join the_table c on a.element_id = c.element_id and c."key" = 'city'
                where a."key" = 'name';




                Another option is to aggregate all key/value pairs for an element into a JSON then you can easily access each one without additional joins:



                select t.element_id, 
                t.obj ->> 'city' as city,
                t.obj ->> 'status' as status,
                t.obj ->> 'name' as name
                from (
                select e.element_id, jsonb_object_agg("key", value) as obj
                from element e
                group by e.element_id
                ) t;


                If the table is really big this might be a lot slower than the join version due to the aggregation step. If you limit the query to only some elements (e.g. by adding a where element_id = 1 or where element_id in (1,2,3)) then this should be quite fast.



                It has the advantage that you always have all key/value pairs for each element_id available regardless on what you do. The inner select could be put into a view, to make things easier.



                Online example: https://rextester.com/MSZOWU37182






                share|improve this answer




























                  0














                  One option is to simply add another join for each value you need (this is one of the big disadvantages of the EAV (anti) pattern you are using:



                  select a.value as name,
                  b.value as status,
                  c.value as city
                  from the_table a
                  join the_table b on a.element_id = b.element_id and b."key" = 'status'
                  join the_table c on a.element_id = c.element_id and c."key" = 'city'
                  where a."key" = 'name';




                  Another option is to aggregate all key/value pairs for an element into a JSON then you can easily access each one without additional joins:



                  select t.element_id, 
                  t.obj ->> 'city' as city,
                  t.obj ->> 'status' as status,
                  t.obj ->> 'name' as name
                  from (
                  select e.element_id, jsonb_object_agg("key", value) as obj
                  from element e
                  group by e.element_id
                  ) t;


                  If the table is really big this might be a lot slower than the join version due to the aggregation step. If you limit the query to only some elements (e.g. by adding a where element_id = 1 or where element_id in (1,2,3)) then this should be quite fast.



                  It has the advantage that you always have all key/value pairs for each element_id available regardless on what you do. The inner select could be put into a view, to make things easier.



                  Online example: https://rextester.com/MSZOWU37182






                  share|improve this answer


























                    0












                    0








                    0







                    One option is to simply add another join for each value you need (this is one of the big disadvantages of the EAV (anti) pattern you are using:



                    select a.value as name,
                    b.value as status,
                    c.value as city
                    from the_table a
                    join the_table b on a.element_id = b.element_id and b."key" = 'status'
                    join the_table c on a.element_id = c.element_id and c."key" = 'city'
                    where a."key" = 'name';




                    Another option is to aggregate all key/value pairs for an element into a JSON then you can easily access each one without additional joins:



                    select t.element_id, 
                    t.obj ->> 'city' as city,
                    t.obj ->> 'status' as status,
                    t.obj ->> 'name' as name
                    from (
                    select e.element_id, jsonb_object_agg("key", value) as obj
                    from element e
                    group by e.element_id
                    ) t;


                    If the table is really big this might be a lot slower than the join version due to the aggregation step. If you limit the query to only some elements (e.g. by adding a where element_id = 1 or where element_id in (1,2,3)) then this should be quite fast.



                    It has the advantage that you always have all key/value pairs for each element_id available regardless on what you do. The inner select could be put into a view, to make things easier.



                    Online example: https://rextester.com/MSZOWU37182






                    share|improve this answer













                    One option is to simply add another join for each value you need (this is one of the big disadvantages of the EAV (anti) pattern you are using:



                    select a.value as name,
                    b.value as status,
                    c.value as city
                    from the_table a
                    join the_table b on a.element_id = b.element_id and b."key" = 'status'
                    join the_table c on a.element_id = c.element_id and c."key" = 'city'
                    where a."key" = 'name';




                    Another option is to aggregate all key/value pairs for an element into a JSON then you can easily access each one without additional joins:



                    select t.element_id, 
                    t.obj ->> 'city' as city,
                    t.obj ->> 'status' as status,
                    t.obj ->> 'name' as name
                    from (
                    select e.element_id, jsonb_object_agg("key", value) as obj
                    from element e
                    group by e.element_id
                    ) t;


                    If the table is really big this might be a lot slower than the join version due to the aggregation step. If you limit the query to only some elements (e.g. by adding a where element_id = 1 or where element_id in (1,2,3)) then this should be quite fast.



                    It has the advantage that you always have all key/value pairs for each element_id available regardless on what you do. The inner select could be put into a view, to make things easier.



                    Online example: https://rextester.com/MSZOWU37182







                    share|improve this answer












                    share|improve this answer



                    share|improve this answer










                    answered Nov 23 '18 at 10:23









                    a_horse_with_no_namea_horse_with_no_name

                    306k46468567




                    306k46468567























                        0














                        Seems like you want to PIVOT



                        One way to do that is via conditional aggregation.



                        select 
                        -- t.element_id,
                        max(case when t.key = 'name' then t.value end) as name,
                        max(case when t.key = 'status' then t.value end) as status,
                        max(case when t.key = 'city' then t.value end) as city
                        from the_table t
                        group by t.element_id;


                        db<>fiddle here



                        Or use crosstab:



                        select 
                        -- element_id,
                        name,
                        status,
                        city
                        from crosstab (
                        'select t.element_id, t.key, t.value
                        from the_table t'
                        ) as ct (element_id int, name varchar(30), status varchar(30), city varchar(30));


                        But if you do like those joins, here's a way



                        select 
                        -- el.element_id,
                        nm.value as name,
                        st.value as status,
                        ci.value as city
                        from
                        (
                        select distinct t.element_id
                        from the_table t
                        where t.key in ('name','status','city')
                        ) as el
                        left join the_table as nm on (nm.element_id = el.element_id and nm.key = 'name')
                        left join the_table as st on (st.element_id = el.element_id and st.key = 'status')
                        left join the_table as ci on (ci.element_id = el.element_id and ci.key = 'city');







                        share|improve this answer






























                          0














                          Seems like you want to PIVOT



                          One way to do that is via conditional aggregation.



                          select 
                          -- t.element_id,
                          max(case when t.key = 'name' then t.value end) as name,
                          max(case when t.key = 'status' then t.value end) as status,
                          max(case when t.key = 'city' then t.value end) as city
                          from the_table t
                          group by t.element_id;


                          db<>fiddle here



                          Or use crosstab:



                          select 
                          -- element_id,
                          name,
                          status,
                          city
                          from crosstab (
                          'select t.element_id, t.key, t.value
                          from the_table t'
                          ) as ct (element_id int, name varchar(30), status varchar(30), city varchar(30));


                          But if you do like those joins, here's a way



                          select 
                          -- el.element_id,
                          nm.value as name,
                          st.value as status,
                          ci.value as city
                          from
                          (
                          select distinct t.element_id
                          from the_table t
                          where t.key in ('name','status','city')
                          ) as el
                          left join the_table as nm on (nm.element_id = el.element_id and nm.key = 'name')
                          left join the_table as st on (st.element_id = el.element_id and st.key = 'status')
                          left join the_table as ci on (ci.element_id = el.element_id and ci.key = 'city');







                          share|improve this answer




























                            0












                            0








                            0







                            Seems like you want to PIVOT



                            One way to do that is via conditional aggregation.



                            select 
                            -- t.element_id,
                            max(case when t.key = 'name' then t.value end) as name,
                            max(case when t.key = 'status' then t.value end) as status,
                            max(case when t.key = 'city' then t.value end) as city
                            from the_table t
                            group by t.element_id;


                            db<>fiddle here



                            Or use crosstab:



                            select 
                            -- element_id,
                            name,
                            status,
                            city
                            from crosstab (
                            'select t.element_id, t.key, t.value
                            from the_table t'
                            ) as ct (element_id int, name varchar(30), status varchar(30), city varchar(30));


                            But if you do like those joins, here's a way



                            select 
                            -- el.element_id,
                            nm.value as name,
                            st.value as status,
                            ci.value as city
                            from
                            (
                            select distinct t.element_id
                            from the_table t
                            where t.key in ('name','status','city')
                            ) as el
                            left join the_table as nm on (nm.element_id = el.element_id and nm.key = 'name')
                            left join the_table as st on (st.element_id = el.element_id and st.key = 'status')
                            left join the_table as ci on (ci.element_id = el.element_id and ci.key = 'city');







                            share|improve this answer















                            Seems like you want to PIVOT



                            One way to do that is via conditional aggregation.



                            select 
                            -- t.element_id,
                            max(case when t.key = 'name' then t.value end) as name,
                            max(case when t.key = 'status' then t.value end) as status,
                            max(case when t.key = 'city' then t.value end) as city
                            from the_table t
                            group by t.element_id;


                            db<>fiddle here



                            Or use crosstab:



                            select 
                            -- element_id,
                            name,
                            status,
                            city
                            from crosstab (
                            'select t.element_id, t.key, t.value
                            from the_table t'
                            ) as ct (element_id int, name varchar(30), status varchar(30), city varchar(30));


                            But if you do like those joins, here's a way



                            select 
                            -- el.element_id,
                            nm.value as name,
                            st.value as status,
                            ci.value as city
                            from
                            (
                            select distinct t.element_id
                            from the_table t
                            where t.key in ('name','status','city')
                            ) as el
                            left join the_table as nm on (nm.element_id = el.element_id and nm.key = 'name')
                            left join the_table as st on (st.element_id = el.element_id and st.key = 'status')
                            left join the_table as ci on (ci.element_id = el.element_id and ci.key = 'city');








                            share|improve this answer














                            share|improve this answer



                            share|improve this answer








                            edited Nov 23 '18 at 11:32

























                            answered Nov 23 '18 at 10:23









                            LukStormsLukStorms

                            14.1k31734




                            14.1k31734






























                                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%2f53444419%2fjoin-three-rows-if-the-same-value-in-one-column%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”?