Updating the SQL key value

Collapse
This topic is closed.
X
X
 
  • Time
  • Show
Clear All
new posts
  • bill

    #1

    Updating the SQL key value

    In an application I am writing the user can define a series of
    steps to be followed. I save them in a sql database using the
    field "order" (a smallint) as the primary key.
    (there are in the range of 20 steps)

    On the admin page the steps are listed, in "order" order and the
    user can create new steps and assign an order and all is well.

    The problem may come in using a renumber function which should
    take the steps in their current order and reassign the "order"
    key, assigning each set an "order" that is the prior "order" + 10.

    In other databases I have worked with this is a major pain
    because as soon as you save a record the order may change.
    OTOH, in mysql it appears that if you do a select for the whole
    table you have them all in memory and can change the value of the
    field "order" without having to worry about getting that record
    again and then save the whole table.

    Is that correct ?

    bill
  • Erwin Moller

    #2
    Re: Updating the SQL key value

    bill wrote:

    Hi Bill,
    In an application I am writing the user can define a series of
    steps to be followed. I save them in a sql database using the
    field "order" (a smallint) as the primary key.
    (there are in the range of 20 steps)
    >
    On the admin page the steps are listed, in "order" order and the
    user can create new steps and assign an order and all is well.
    >
    The problem may come in using a renumber function which should
    take the steps in their current order and reassign the "order"
    key, assigning each set an "order" that is the prior "order" + 10.
    Well, what about:

    UPDATE tblwhatever set order = order+10;

    >
    In other databases I have worked with this is a major pain
    because as soon as you save a record the order may change.
    I never heard of a database that changes the values of other rows if you
    insert a new row.
    Are you maybe using this order filed as Primary Key?

    If so: That is a major designmistake.
    (I'll elaborate if this turns out to be the case.)
    OTOH, in mysql it appears that if you do a select for the whole
    table you have them all in memory and can change the value of the
    field "order" without having to worry about getting that record
    again and then save the whole table.
    >
    Is that correct ?
    No.

    A query is a query: something that return results (or none).
    If you SELECT something, nothing is changed.

    I don't understand what you mean by 'selecting the whole table in memory'.
    If you need to UPDATE certain records, use the UPDATE command to do so.
    >
    bill
    Regards,
    Erwin Moller

    Comment

    • Jerry Stuckle

      #3
      Re: Updating the SQL key value

      bill wrote:
      In an application I am writing the user can define a series of steps to
      be followed. I save them in a sql database using the field "order" (a
      smallint) as the primary key.
      (there are in the range of 20 steps)
      >
      On the admin page the steps are listed, in "order" order and the user
      can create new steps and assign an order and all is well.
      >
      The problem may come in using a renumber function which should take the
      steps in their current order and reassign the "order" key, assigning
      each set an "order" that is the prior "order" + 10.
      >
      In other databases I have worked with this is a major pain because as
      soon as you save a record the order may change.
      OTOH, in mysql it appears that if you do a select for the whole table
      you have them all in memory and can change the value of the field
      "order" without having to worry about getting that record again and then
      save the whole table.
      >
      Is that correct ?
      >
      bill
      Bill,

      Try asking in comp.databases. mysql. This is a PHP newsgroup.

      --
      =============== ===
      Remove the "x" from my email address
      Jerry Stuckle
      JDS Computer Training Corp.
      jstucklex@attgl obal.net
      =============== ===

      Comment

      • bill

        #4
        Re: Updating the SQL key value

        Erwin Moller wrote:
        bill wrote:
        >
        Hi Bill,
        >
        >In an application I am writing the user can define a series of
        >steps to be followed. I save them in a sql database using the
        >field "order" (a smallint) as the primary key.
        >(there are in the range of 20 steps)
        >>
        >On the admin page the steps are listed, in "order" order and the
        >user can create new steps and assign an order and all is well.
        >>
        >The problem may come in using a renumber function which should
        >take the steps in their current order and reassign the "order"
        >key, assigning each set an "order" that is the prior "order" + 10.
        >
        Well, what about:
        >
        UPDATE tblwhatever set order = order+10;
        I think I was unclear:
        the order might evolve to:

        10
        12
        16
        18
        30
        33

        and after renumbering it should be back to
        10
        20
        30
        40
        50
        60
        >
        >
        >In other databases I have worked with this is a major pain
        >because as soon as you save a record the order may change.
        >
        I never heard of a database that changes the values of other rows if you
        insert a new row.
        Are you maybe using this order filed as Primary Key?
        >
        If so: That is a major designmistake.
        (I'll elaborate if this turns out to be the case.)
        guilty.
        That is why the updating of the "order" would result in the
        problem.

        If I use another primary key (an arbitrary ID) then I still have
        to read the records in "order" number and reassign the value of
        "order" and then read the next record.
        >
        >OTOH, in mysql it appears that if you do a select for the whole
        >table you have them all in memory and can change the value of the
        >field "order" without having to worry about getting that record
        >again and then save the whole table.
        >>
        >Is that correct ?
        >
        No.
        bummer
        >
        A query is a query: something that return results (or none).
        If you SELECT something, nothing is changed.
        >
        I don't understand what you mean by 'selecting the whole table in memory'.
        If you need to UPDATE certain records, use the UPDATE command to do so.
        but after having UPDATEd, one still has the problem of reading
        the next record
        >
        >bill
        >
        Regards,
        Erwin Moller
        your assistance is appreciated

        bill

        Comment

        • Erwin Moller

          #5
          Re: Updating the SQL key value

          bill wrote:
          Erwin Moller wrote:
          >bill wrote:
          >>
          >Hi Bill,
          >>
          >>In an application I am writing the user can define a series of
          >>steps to be followed. I save them in a sql database using the
          >>field "order" (a smallint) as the primary key.
          >>(there are in the range of 20 steps)
          >>>
          >>On the admin page the steps are listed, in "order" order and the
          >>user can create new steps and assign an order and all is well.
          >>>
          >>The problem may come in using a renumber function which should
          >>take the steps in their current order and reassign the "order"
          >>key, assigning each set an "order" that is the prior "order" + 10.
          >>
          >Well, what about:
          >>
          >UPDATE tblwhatever set order = order+10;
          >
          I think I was unclear:
          the order might evolve to:
          >
          10
          12
          16
          18
          30
          33
          >
          and after renumbering it should be back to
          10
          20
          30
          40
          50
          60
          Aha. I see now what you mean.
          Hmm, that is tricky to do with 1 UPDATE statement since you didn't use a
          'normal' autonumbering Primary Key.


          I think you have to resort to a simple script to do that for you.

          Can we assume that you NEVER have any double values for 'order'?
          I mean, did you make that field PK or at least UNIQUE? Or as a last resort,
          check before inserting that the 'order' didn't exist yet?

          If they are all UNIQUE try something like:
          -- pseudocode ADODB-like, adjust to your own prefered databaselogic

          $SQL = "SELECT order FROM tblorder ORDER BY order";
          $result = $connection->Execute($SQL )->getArray();

          $count = 10;
          foreach ($result as $oneOrder){
          $oldOrderNum = $oneOrder["order"];
          // Update
          $SQL = "UPDATE tblorder SET order=".$count;
          $SQL .= " WHERE (order=".$oldOr derNum.");";
          $connection->Execute($SQL );
          }


          >>
          >>
          >>In other databases I have worked with this is a major pain
          >>because as soon as you save a record the order may change.
          >>
          >I never heard of a database that changes the values of other rows if you
          >insert a new row.
          >Are you maybe using this order filed as Primary Key?
          >>
          >If so: That is a major designmistake.
          >(I'll elaborate if this turns out to be the case.)
          >
          guilty.
          That is why the updating of the "order" would result in the
          problem.
          >
          If I use another primary key (an arbitrary ID) then I still have
          to read the records in "order" number and reassign the value of
          "order" and then read the next record.
          In this case: maybe.

          But take it from an old db-fart like me: Start using an autonumbering PK for
          each table you create from now on, unless you have a compelling reason not
          to. I actually was never in a situation where an autonumbering PK hurts.
          Only in some cases you really know it doesn't make sense (scratch tables,
          temp tables, etc).
          As a rule of thumb: Use autonumbering PK on every table.

          In my above example I wouldn't need to be afraid you have some values for
          'order' that are the same (which would make that approach fail).
          A autonumbering PK would have eliminated that problem.

          >>
          >>OTOH, in mysql it appears that if you do a select for the whole
          >>table you have them all in memory and can change the value of the
          >>field "order" without having to worry about getting that record
          >>again and then save the whole table.
          >>>
          >>Is that correct ?
          >>
          >No.
          >
          bummer
          Unless you mean my above script. That takes the all 'order' values from
          tblorder in memory, but that is independent from the database, it is PHP's
          memory.
          >>
          >A query is a query: something that return results (or none).
          >If you SELECT something, nothing is changed.
          >>
          >I don't understand what you mean by 'selecting the whole table in
          >memory'. If you need to UPDATE certain records, use the UPDATE command to
          >do so.
          >
          but after having UPDATEd, one still has the problem of reading
          the next record
          Not if you read them all at once first into memory of php and start updating
          then.
          >>
          >>bill
          >>
          >Regards,
          >Erwin Moller
          >
          your assistance is appreciated
          >
          You're welcome
          bill
          Regards,
          Erwin Moller

          Comment

          • Toby A Inkster

            #6
            Re: Updating the SQL key value

            bill wrote:
            The problem may come in using a renumber function which should
            take the steps in their current order and reassign the "order"
            key, assigning each set an "order" that is the prior "order" + 10.
            Something like:

            <?php
            $db = new PDO(/* connection settings */);
            $db->query("ALTER TABLE foobar ADD tempcol integer;");
            $update = $db->prepare("UPDAT E foobar SET tempcol=? WHERE order=?;");
            $count = 0;
            foreach ($db->query("SELEC T order FROM foobar ORDER BY order;") as $row)
            {
            $count+=10;
            $update->execute($count , $row['order']);
            }
            $db->query("UPDAT E foobar SET order=tempcol;" );
            $db->query("ALTER TABLE foobar DROP tempcol;");
            unset($count, $update);
            ?>

            --
            Toby A Inkster BSc (Hons) ARCS
            Contact Me ~ http://tobyinkster.co.uk/contact
            Geek of ~ HTML/SQL/Perl/PHP/Python*/Apache/Linux

            * = I'm getting there!

            Comment

            • Drazen Gemic

              #7
              Re: Updating the SQL key value

              On Mar 28, 3:50 pm, bill <nob...@spamcop .netwrote:
              In other databases I have worked with this is a major pain
              because as soon as you save a record the order may change.
              OTOH, in mysql it appears that if you do a select for the whole
              table you have them all in memory and can change the value of the
              field "order" without having to worry about getting that record
              again and then save the whole table.
              I am working on similar application, where I have steps in a certain
              business process, but I don't use order number as a primary key.

              I don't think that you shoud rely on such MySQL feature,
              because it could change in next release.

              My advice is to always repeat the query after renumbering.

              DG

              Comment

              • bill

                #8
                Re: Updating the SQL key value

                Erwin Moller wrote:
                bill wrote:
                >
                >Erwin Moller wrote:
                >>bill wrote:
                >>>
                >>Hi Bill,
                >>>
                >>>In an application I am writing the user can define a series of
                >>>steps to be followed. I save them in a sql database using the
                >>>field "order" (a smallint) as the primary key.
                >>>(there are in the range of 20 steps)
                >>>>
                >>>On the admin page the steps are listed, in "order" order and the
                >>>user can create new steps and assign an order and all is well.
                >>>>
                >>>The problem may come in using a renumber function which should
                >>>take the steps in their current order and reassign the "order"
                >>>key, assigning each set an "order" that is the prior "order" + 10.
                >>Well, what about:
                >>>
                >>UPDATE tblwhatever set order = order+10;
                >I think I was unclear:
                >the order might evolve to:
                >>
                >10
                >12
                >16
                >18
                >30
                >33
                >>
                >and after renumbering it should be back to
                >10
                >20
                >30
                >40
                >50
                >60
                >
                Aha. I see now what you mean.
                Hmm, that is tricky to do with 1 UPDATE statement since you didn't use a
                'normal' autonumbering Primary Key.
                >
                >
                I think you have to resort to a simple script to do that for you.
                >
                Can we assume that you NEVER have any double values for 'order'?
                I mean, did you make that field PK or at least UNIQUE? Or as a last resort,
                check before inserting that the 'order' didn't exist yet?
                >
                If they are all UNIQUE try something like:
                -- pseudocode ADODB-like, adjust to your own prefered databaselogic
                >
                $SQL = "SELECT order FROM tblorder ORDER BY order";
                $result = $connection->Execute($SQL )->getArray();
                >
                $count = 10;
                foreach ($result as $oneOrder){
                $oldOrderNum = $oneOrder["order"];
                // Update
                $SQL = "UPDATE tblorder SET order=".$count;
                $SQL .= " WHERE (order=".$oldOr derNum.");";
                $connection->Execute($SQL );
                }
                >
                >
                >
                >>>
                >>>In other databases I have worked with this is a major pain
                >>>because as soon as you save a record the order may change.
                >>I never heard of a database that changes the values of other rows if you
                >>insert a new row.
                >>Are you maybe using this order filed as Primary Key?
                >>>
                >>If so: That is a major designmistake.
                >>(I'll elaborate if this turns out to be the case.)
                >guilty.
                >That is why the updating of the "order" would result in the
                >problem.
                >>
                >If I use another primary key (an arbitrary ID) then I still have
                >to read the records in "order" number and reassign the value of
                >"order" and then read the next record.
                >
                In this case: maybe.
                >
                But take it from an old db-fart like me: Start using an autonumbering PK for
                each table you create from now on, unless you have a compelling reason not
                to. I actually was never in a situation where an autonumbering PK hurts.
                Only in some cases you really know it doesn't make sense (scratch tables,
                temp tables, etc).
                As a rule of thumb: Use autonumbering PK on every table.
                >
                In my above example I wouldn't need to be afraid you have some values for
                'order' that are the same (which would make that approach fail).
                A autonumbering PK would have eliminated that problem.
                Understood. I will add a autonumbering PK
                >
                >
                >>>OTOH, in mysql it appears that if you do a select for the whole
                >>>table you have them all in memory and can change the value of the
                >>>field "order" without having to worry about getting that record
                >>>again and then save the whole table.
                >>>>
                >>>Is that correct ?
                >>No.
                >bummer
                >
                Unless you mean my above script. That takes the all 'order' values from
                tblorder in memory, but that is independent from the database, it is PHP's
                memory.
                Works for me.
                Thanks
                >
                >>A query is a query: something that return results (or none).
                >>If you SELECT something, nothing is changed.
                >>>
                >>I don't understand what you mean by 'selecting the whole table in
                >>memory'. If you need to UPDATE certain records, use the UPDATE command to
                >>do so.
                >but after having UPDATEd, one still has the problem of reading
                >the next record
                >
                Not if you read them all at once first into memory of php and start updating
                then.
                >
                >>>bill
                >>Regards,
                >>Erwin Moller
                >your assistance is appreciated
                >>
                >
                You're welcome
                >
                >bill
                >
                Regards,
                Erwin Moller
                I certainly appreciate the tutorial. I understand the internals
                of mySQL just a little bit better.

                bill

                Comment

                • bill

                  #9
                  Re: Updating the SQL key value

                  Toby A Inkster wrote:
                  bill wrote:
                  >
                  >The problem may come in using a renumber function which should
                  >take the steps in their current order and reassign the "order"
                  >key, assigning each set an "order" that is the prior "order" + 10.
                  >
                  Something like:
                  >
                  <?php
                  $db = new PDO(/* connection settings */);
                  $db->query("ALTER TABLE foobar ADD tempcol integer;");
                  $update = $db->prepare("UPDAT E foobar SET tempcol=? WHERE order=?;");
                  $count = 0;
                  foreach ($db->query("SELEC T order FROM foobar ORDER BY order;") as $row)
                  {
                  $count+=10;
                  $update->execute($count , $row['order']);
                  }
                  $db->query("UPDAT E foobar SET order=tempcol;" );
                  $db->query("ALTER TABLE foobar DROP tempcol;");
                  unset($count, $update);
                  ?>
                  >
                  Lovely idea. For my small number a PHP array probably works
                  better, but I do like this approach and will archive it.

                  One additional question: does the foreach loop terminate because
                  the query return false ?

                  bill

                  Comment

                  • Toby A Inkster

                    #10
                    Re: Updating the SQL key value

                    bill wrote:
                    One additional question: does the foreach loop terminate because
                    the query return false ?
                    It terminates as soon as the query has run out of rows.

                    It's a PHP 5 feature called "Iterators" -- that is PDO queries are
                    returned as objects that, although they are not arrays, can be treated as
                    arrays in a foreach loop. The solution would work equally well using PHP 4
                    constructs, but the code doesn't look as neat.

                    --
                    Toby A Inkster BSc (Hons) ARCS
                    Contact Me ~ http://tobyinkster.co.uk/contact
                    Geek of ~ HTML/SQL/Perl/PHP/Python*/Apache/Linux

                    * = I'm getting there!

                    Comment

                    • Toby A Inkster

                      #11
                      Re: Updating the SQL key value

                      bill wrote:
                      I will add a autonumbering PK
                      Auto-numbered synthetic primary keys are the root of all evil. They usually
                      indicate a lazy approach to database design. There is almost always a
                      natural column or combination of columns that can be used as a primary key
                      without the need to add an extra, redundant numerical column which doesn't
                      contain any useful information.

                      Assuming that the you never plan on having two events that occur
                      concurrently, your "order" column is a perfect natural candidate key.
                      (Though it's a little poorly named, given that ORDER is a SQL keyword.)

                      --
                      Toby A Inkster BSc (Hons) ARCS
                      Contact Me ~ http://tobyinkster.co.uk/contact
                      Geek of ~ HTML/SQL/Perl/PHP/Python*/Apache/Linux

                      * = I'm getting there!

                      Comment

                      • Erwin Moller

                        #12
                        Re: Updating the SQL key value

                        Toby A Inkster wrote:
                        bill wrote:
                        >
                        >I will add a autonumbering PK
                        >
                        Auto-numbered synthetic primary keys are the root of all evil. They
                        usually indicate a lazy approach to database design. There is almost
                        always a natural column or combination of columns that can be used as a
                        primary key without the need to add an extra, redundant numerical column
                        which doesn't contain any useful information.
                        >
                        Assuming that the you never plan on having two events that occur
                        concurrently, your "order" column is a perfect natural candidate key.
                        (Though it's a little poorly named, given that ORDER is a SQL keyword.)
                        >
                        Hi Toby,

                        Despite my high respect for you: I completely disagree.
                        Why picking 'natural candidates' if you can make it work ALWAYS with a
                        simple autonumbering PK?
                        What if your database must be upgraded and the logic changes?
                        Do you want to check all the columns again to be sure the PK still makes
                        sense? (Or watch it fail in a production environment when the UNIQUE
                        constraint is hit you didn't see coming beforehand)

                        I have been using autonumbering PK my whole programming carrier, and never
                        had any problems with it.

                        Who seriously cares about the few extra bytes needed?

                        I am not alone with that thought.
                        Postgres even makes an OID for each row, something you don't even see but
                        can use if you want.

                        It is perfectly natural IMO when designing a database to point to each row
                        in a simple, coherent, and easy to understand fashion by using
                        autonumbering PKs.
                        It also makes it very easy to use FK.

                        I don't get it why you think of that as 'the root of all evil'.
                        What evil comes out of it?

                        Regards,
                        Erwin Moller

                        Comment

                        • Toby A Inkster

                          #13
                          Re: Updating the SQL key value

                          Erwin Moller wrote:
                          Why picking 'natural candidates' if you can make it work ALWAYS with a
                          simple autonumbering PK?
                          As someone who normally spends quite some time refining my database schema
                          before creating the database, by the time I've come to pick a primary key,
                          I've normally already decided on one or two UNIQUE constraints, so it's a
                          simple matter of deciding which of the UNIQUE constraints is fit to be the
                          primary key.

                          Besides, there are other techniques that can be said to "always work". For
                          example, the creation of a varchar column "code" as a primary key. It's
                          still a surrogate key really, but it can take on more meaning than an
                          auto-numbered surrogate.

                          For example, in one of my current projects, I need to store several
                          articles, each of which must be issued under a particular licence (e.g.
                          GPL, FDL, Creatice Commons). The "auto-number everything" solution would
                          be:

                          =============== =============== ========
                          table: articles
                          --------------------------------------
                          article_id integer, autonumbered
                          title varchar
                          body varchar
                          licence integer
                          =============== =============== ========

                          =============== =============== ========
                          table: licences
                          --------------------------------------
                          licence_id integer
                          licence_name varchar
                          licence_link varchar
                          =============== =============== ========

                          Example data in table licences:

                          1 GNU General Public Licence http://www.gnu...
                          2 GNU Free Documentation Licence http://www.gnu...
                          3 Creative Commons Licence http://www.cre...

                          Using a manually-named varchar surrogate primary key, you could have

                          =============== =============== ========
                          table: articles
                          --------------------------------------
                          article_id integer, autonumbered
                          title varchar
                          body varchar
                          licence char(8)
                          =============== =============== ========

                          =============== =============== ========
                          table: licences
                          --------------------------------------
                          licence_code char(8)
                          licence_name varchar
                          licence_link varchar
                          =============== =============== ========

                          Example data in table licences:

                          GPL GNU General Public Licence http://www.gnu...
                          FDL GNU Free Documentation Licence http://www.gnu...
                          CC Creative Commons Licence http://www.cre...

                          Usage is fairly similar, apart from the fact that now, when you look at
                          the table 'articles' without doing any joins, you can still infer a bit of
                          information about which licence each article is under, without having to
                          inner join onto the licences table.

                          This isn't *always* a good approach, but it's often a lot better than an
                          auto-numbered key.

                          And before you say that this is a waste of space as char(8) takes up eight
                          bytes rather than 4 bytes for an integer, you're second-guessing the
                          database engine there. Database engines really are dead clever. Most will
                          only store the full char(8) string in the licences table (likely to be
                          quite small compared to the articles table), and when storing the licence
                          column of the articles table will actually use a pointer back to the same
                          string data from the licence table -- very fast. Database engines really
                          are dead clever. (And as far as sorting is concerned, a short, indexed
                          char field is just as fast as an integer.)
                          What if your database must be upgraded and the logic changes?
                          Do you want to check all the columns again to be sure the PK still makes
                          sense? (Or watch it fail in a production environment when the UNIQUE
                          constraint is hit you didn't see coming beforehand)
                          When the logic changes in some major way, you're probably going to need to
                          make adjustments to several tables anyway. I don't see this as a major
                          problem.

                          Besides which, it's often quite easy to choose a column that will always
                          be unique. For example for a table of users, instead of:

                          user_id auto_increment (primary key)
                          login varchar
                          password varchar
                          realname varchar
                          email_address varchar

                          All you need is:

                          login varchar (primary key)
                          password varchar
                          realname varchar
                          email_address varchar

                          Whatsmore, say you then have another table which has a column that has a
                          foreign key for your user table, looking down that column you don't see a
                          bunch of numbers like "12, 14, 71, 14" -- you see "brian, dave, greg, dave".

                          Logic changing is never going to be a problem -- you're never going to
                          have two users with the same login name.
                          I have been using autonumbering PK my whole programming carrier, and never
                          had any problems with it.
                          No doubt -- the problem occurs when people blindly use surrogate keys
                          without thinking about whether the rest of their columns could be keys --
                          which I'm sure you'd never do Erwin! :-)

                          By doing that, they put less thought into UNIQUE constraints, and end up
                          with lots of unwanted rows which would be duplicates, except for their
                          primary key. If they hadn't added that extra primary key number, then they
                          wouldn't have a database full of duplicate values.
                          Who seriously cares about the few extra bytes needed?
                          Not me.
                          I am not alone with that thought.
                          Postgres even makes an OID for each row, something you don't even see but
                          can use if you want.
                          It's perfectly easy to disable OIDs in PostgreSQL on a case-by-case basis
                          or permanently. Most of the time, I disable OIDs.

                          On one of my current projects, I've got twelve tables, only one of which
                          has a surrogate integer primary key (technically it doesn't autonumber,
                          but I use MAX() to simulate autonumbering when creating a new record).
                          Guess which table is causing me the most problems?

                          It's a table of articles. A table of comments references the articles via
                          its numeric key. Now what happens if I write an updated version of an
                          article, but want to keep a copy of the old one? I mark a status flag on
                          the old one to hide it, then create another article with the same URL but
                          a different ID number. Unfortunately the comments still point to the old
                          ID number, so are not seen when you visit the new page.

                          Better would have been to design my table so that it had a primary key
                          like (url, revision).

                          Anyhow, it's mostly a matter of taste. I was being somewhat tongue in
                          cheek when describing surrogate keys as "the root of all evil", but I
                          can't stand to see a good candidate key go to waste.

                          --
                          Toby A Inkster BSc (Hons) ARCS
                          Contact Me ~ http://tobyinkster.co.uk/contact
                          Geek of ~ HTML/SQL/Perl/PHP/Python*/Apache/Linux

                          * = I'm getting there!

                          Comment

                          • Erwin Moller

                            #14
                            Re: Updating the SQL key value

                            Toby A Inkster wrote:
                            Erwin Moller wrote:
                            >
                            >Why picking 'natural candidates' if you can make it work ALWAYS with a
                            >simple autonumbering PK?
                            >
                            Hi Toby,
                            As someone who normally spends quite some time refining my database schema
                            before creating the database, by the time I've come to pick a primary key,
                            I've normally already decided on one or two UNIQUE constraints, so it's a
                            simple matter of deciding which of the UNIQUE constraints is fit to be the
                            primary key.
                            >
                            Besides, there are other techniques that can be said to "always work". For
                            example, the creation of a varchar column "code" as a primary key. It's
                            still a surrogate key really, but it can take on more meaning than an
                            auto-numbered surrogate.
                            >
                            For example, in one of my current projects, I need to store several
                            articles, each of which must be issued under a particular licence (e.g.
                            GPL, FDL, Creatice Commons). The "auto-number everything" solution would
                            be:
                            >
                            =============== =============== ========
                            table: articles
                            --------------------------------------
                            article_id integer, autonumbered
                            title varchar
                            body varchar
                            licence integer
                            =============== =============== ========
                            >
                            =============== =============== ========
                            table: licences
                            --------------------------------------
                            licence_id integer
                            licence_name varchar
                            licence_link varchar
                            =============== =============== ========
                            >
                            Example data in table licences:
                            >
                            1 GNU General Public Licence http://www.gnu...
                            2 GNU Free Documentation Licence http://www.gnu...
                            3 Creative Commons Licence http://www.cre...
                            >
                            Using a manually-named varchar surrogate primary key, you could have
                            >
                            =============== =============== ========
                            table: articles
                            --------------------------------------
                            article_id integer, autonumbered
                            title varchar
                            body varchar
                            licence char(8)
                            =============== =============== ========
                            >
                            =============== =============== ========
                            table: licences
                            --------------------------------------
                            licence_code char(8)
                            licence_name varchar
                            licence_link varchar
                            =============== =============== ========
                            >
                            Example data in table licences:
                            >
                            GPL GNU General Public Licence http://www.gnu...
                            FDL GNU Free Documentation Licence http://www.gnu...
                            CC Creative Commons Licence http://www.cre...
                            >
                            Usage is fairly similar, apart from the fact that now, when you look at
                            the table 'articles' without doing any joins, you can still infer a bit of
                            information about which licence each article is under, without having to
                            inner join onto the licences table.
                            >
                            This isn't *always* a good approach, but it's often a lot better than an
                            auto-numbered key.
                            OK, clear example.
                            This has the advantage you have more descriptive PKs.

                            >
                            And before you say that this is a waste of space as char(8) takes up eight
                            bytes rather than 4 bytes for an integer, you're second-guessing the
                            database engine there.
                            I won't say that. :-)
                            I never care too much about a byte or two extra if it increases readability
                            or structure, etc.


                            Database engines really are dead clever. Most will
                            only store the full char(8) string in the licences table (likely to be
                            quite small compared to the articles table), and when storing the licence
                            column of the articles table will actually use a pointer back to the same
                            string data from the licence table -- very fast. Database engines really
                            are dead clever. (And as far as sorting is concerned, a short, indexed
                            char field is just as fast as an integer.)
                            Yes, B-tree index lookups (and the like) are extremely fast.
                            >
                            >What if your database must be upgraded and the logic changes?
                            >Do you want to check all the columns again to be sure the PK still makes
                            >sense? (Or watch it fail in a production environment when the UNIQUE
                            >constraint is hit you didn't see coming beforehand)
                            >
                            When the logic changes in some major way, you're probably going to need to
                            make adjustments to several tables anyway. I don't see this as a major
                            problem.
                            >
                            Besides which, it's often quite easy to choose a column that will always
                            be unique. For example for a table of users, instead of:
                            >
                            user_id auto_increment (primary key)
                            login varchar
                            password varchar
                            realname varchar
                            email_address varchar
                            >
                            All you need is:
                            >
                            login varchar (primary key)
                            password varchar
                            realname varchar
                            email_address varchar
                            >
                            Whatsmore, say you then have another table which has a column that has a
                            foreign key for your user table, looking down that column you don't see a
                            bunch of numbers like "12, 14, 71, 14" -- you see "brian, dave, greg,
                            dave".
                            Well, a simple join solves that.
                            >
                            Logic changing is never going to be a problem -- you're never going to
                            have two users with the same login name.
                            Well, you gave me excactly an example where your approach is weaker than
                            using autonumbering PKs, I think.

                            Consider this example:
                            1) Your tbluser as described above by you.
                            2) a collection of articles written by that user

                            CREATE TABLE tblarticle(
                            articleid serial PRIMARY KEY,
                            writtenby login REFERENCES tbluser(login),
                            content VARCHAR(10000)
                            )

                            I made a FK as you see.
                            Now what happens if that user wants a new loginname?

                            You are forced to update this new loginname everwhere in your database where
                            you use it.
                            Or do you use cascading?
                            (I dislike cascading though all FK constraints, but it is of course
                            possible)


                            >
                            >I have been using autonumbering PK my whole programming carrier, and
                            >never had any problems with it.
                            >
                            No doubt -- the problem occurs when people blindly use surrogate keys
                            without thinking about whether the rest of their columns could be keys --
                            which I'm sure you'd never do Erwin! :-)
                            ;-)

                            >
                            By doing that, they put less thought into UNIQUE constraints, and end up
                            with lots of unwanted rows which would be duplicates, except for their
                            primary key. If they hadn't added that extra primary key number, then they
                            wouldn't have a database full of duplicate values.
                            Well, that is normalization.
                            The more databases you design, the better you get at grouping stuff and
                            making entities.
                            I never store a piece of information double.
                            Almost never, unless the joining will take that much calculation that I
                            think it is better to denormalize a little.

                            >
                            >Who seriously cares about the few extra bytes needed?
                            >
                            Not me.
                            >
                            >I am not alone with that thought.
                            >Postgres even makes an OID for each row, something you don't even see but
                            >can use if you want.
                            >
                            It's perfectly easy to disable OIDs in PostgreSQL on a case-by-case basis
                            or permanently. Most of the time, I disable OIDs.
                            >
                            On one of my current projects, I've got twelve tables, only one of which
                            has a surrogate integer primary key (technically it doesn't autonumber,
                            but I use MAX() to simulate autonumbering when creating a new record).
                            What happened to good old nextval()?
                            Guess which table is causing me the most problems?
                            The other ones? ;-)
                            >
                            It's a table of articles. A table of comments references the articles via
                            its numeric key. Now what happens if I write an updated version of an
                            article, but want to keep a copy of the old one? I mark a status flag on
                            the old one to hide it, then create another article with the same URL but
                            a different ID number. Unfortunately the comments still point to the old
                            ID number, so are not seen when you visit the new page.
                            >
                            Better would have been to design my table so that it had a primary key
                            like (url, revision).
                            Yes, that would be a nice solution.

                            I would typically do something like this:

                            CREATE TABLE revisions(
                            revisionid SERIAL PRIMARY KEY,
                            content VARCHAR(10000),
                            createdate timestamp
                            )

                            CREATE TABLE tblarticle(
                            articleid SERIAL PRIMARY KEY,
                            url varchar(1000),
                            activerevisioni d integer REFERENCES tblrevisions(re visionid)
                            )

                            CREATE TABLE comments(
                            commentid SERIAL PRIMARY KEY,
                            comment VARCHAR(1000),
                            articleid integer REFERENCES tblarticle(arti cleid)
                            )

                            So you have your comments pointing at the article, and not the revision.
                            But, as you say, this is a matter of taste.
                            >
                            Anyhow, it's mostly a matter of taste. I was being somewhat tongue in
                            cheek when describing surrogate keys as "the root of all evil", but I
                            can't stand to see a good candidate key go to waste.
                            :-)
                            Clear.

                            I liked this discussion.
                            Once I tauch myself to work with autonumbering PK and good normalization and
                            FKs, I never gave the Primary Key much second thought.
                            Always good to hear other opinions.

                            Best of luck!

                            Regards,
                            Erwin Moller

                            Comment

                            • Jerry Stuckle

                              #15
                              Re: Updating the SQL key value

                              Toby A Inkster wrote:
                              Erwin Moller wrote:
                              >
                              >Why picking 'natural candidates' if you can make it work ALWAYS with a
                              >simple autonumbering PK?
                              >
                              As someone who normally spends quite some time refining my database schema
                              before creating the database, by the time I've come to pick a primary key,
                              I've normally already decided on one or two UNIQUE constraints, so it's a
                              simple matter of deciding which of the UNIQUE constraints is fit to be the
                              primary key.
                              >
                              Besides, there are other techniques that can be said to "always work". For
                              example, the creation of a varchar column "code" as a primary key. It's
                              still a surrogate key really, but it can take on more meaning than an
                              auto-numbered surrogate.
                              >

                              Hi, Toby, and I have to agree with Erwin. As much as I respect you, I
                              have to disagree. Please see below...

                              For example, in one of my current projects, I need to store several
                              articles, each of which must be issued under a particular licence (e.g.
                              GPL, FDL, Creatice Commons). The "auto-number everything" solution would
                              be:
                              >
                              =============== =============== ========
                              table: articles
                              --------------------------------------
                              article_id integer, autonumbered
                              title varchar
                              body varchar
                              licence integer
                              =============== =============== ========
                              >
                              =============== =============== ========
                              table: licences
                              --------------------------------------
                              licence_id integer
                              licence_name varchar
                              licence_link varchar
                              =============== =============== ========
                              >
                              Example data in table licences:
                              >
                              1 GNU General Public Licence http://www.gnu...
                              2 GNU Free Documentation Licence http://www.gnu...
                              3 Creative Commons Licence http://www.cre...
                              >
                              Using a manually-named varchar surrogate primary key, you could have
                              >
                              =============== =============== ========
                              table: articles
                              --------------------------------------
                              article_id integer, autonumbered
                              title varchar
                              body varchar
                              licence char(8)
                              =============== =============== ========
                              >
                              =============== =============== ========
                              table: licences
                              --------------------------------------
                              licence_code char(8)
                              licence_name varchar
                              licence_link varchar
                              =============== =============== ========
                              >
                              Example data in table licences:
                              >
                              GPL GNU General Public Licence http://www.gnu...
                              FDL GNU Free Documentation Licence http://www.gnu...
                              CC Creative Commons Licence http://www.cre...
                              >
                              Usage is fairly similar, apart from the fact that now, when you look at
                              the table 'articles' without doing any joins, you can still infer a bit of
                              information about which licence each article is under, without having to
                              inner join onto the licences table.
                              >
                              True, but you also have to look at performance issues. When searching
                              an index, comparing an int is always faster than comparing a varchar.
                              And comparing a single column is always faster than comparing multiple
                              columns. And the index file itself is smaller.
                              This isn't *always* a good approach, but it's often a lot better than an
                              auto-numbered key.
                              >
                              Additionally, the PK should not be dependent on data which may change -
                              i.e. if part of your key was the license (code), what would happen if
                              they changed the licensing terms?
                              And before you say that this is a waste of space as char(8) takes up eight
                              bytes rather than 4 bytes for an integer, you're second-guessing the
                              database engine there. Database engines really are dead clever. Most will
                              only store the full char(8) string in the licences table (likely to be
                              quite small compared to the articles table), and when storing the licence
                              column of the articles table will actually use a pointer back to the same
                              string data from the licence table -- very fast. Database engines really
                              are dead clever. (And as far as sorting is concerned, a short, indexed
                              char field is just as fast as an integer.)
                              >
                              Not generally, they don't, because of potential problems. For instance,
                              if the data is stored as a pointer to the license table, the system
                              has to do an additional file lookup to fetch the license data. And what
                              happens if the entry from the license table is altered - or worse yet,
                              deleted? There is no referential integrity built in here. Deleting an
                              item from the license table would require all other tables which point
                              to that entry be updated - that is, the varchar data would have to be
                              reinserted into each row in every table which pointed to the license table.

                              And in your case, the data would be stored in 4 bytes ("GPL" + 1 byte
                              length). So it would take the same 4 bytes - but comparisons would
                              still be slower.
                              >What if your database must be upgraded and the logic changes?
                              >Do you want to check all the columns again to be sure the PK still makes
                              >sense? (Or watch it fail in a production environment when the UNIQUE
                              >constraint is hit you didn't see coming beforehand)
                              >
                              When the logic changes in some major way, you're probably going to need to
                              make adjustments to several tables anyway. I don't see this as a major
                              problem.
                              >
                              Even when logic changes I don't generally have to change table design,
                              other than to perhaps add a column or two. Good normalization
                              techniques help here.
                              Besides which, it's often quite easy to choose a column that will always
                              be unique. For example for a table of users, instead of:
                              >
                              user_id auto_increment (primary key)
                              login varchar
                              password varchar
                              realname varchar
                              email_address varchar
                              >
                              All you need is:
                              >
                              login varchar (primary key)
                              password varchar
                              realname varchar
                              email_address varchar
                              >
                              Whatsmore, say you then have another table which has a column that has a
                              foreign key for your user table, looking down that column you don't see a
                              bunch of numbers like "12, 14, 71, 14" -- you see "brian, dave, greg, dave".
                              >
                              Logic changing is never going to be a problem -- you're never going to
                              have two users with the same login name.
                              >
                              That's true. But you're also taking up more storage space and slowing
                              down searches.
                              >I have been using autonumbering PK my whole programming carrier, and never
                              >had any problems with it.
                              >
                              No doubt -- the problem occurs when people blindly use surrogate keys
                              without thinking about whether the rest of their columns could be keys --
                              which I'm sure you'd never do Erwin! :-)
                              >
                              By doing that, they put less thought into UNIQUE constraints, and end up
                              with lots of unwanted rows which would be duplicates, except for their
                              primary key. If they hadn't added that extra primary key number, then they
                              wouldn't have a database full of duplicate values.
                              >
                              You still can (and should) put serious thought into unique constraints.
                              Just having a separate PK shouldn't change that.
                              >Who seriously cares about the few extra bytes needed?
                              >
                              Not me.
                              >
                              I do. When it comes to very large databases, this can add up very
                              quickly. And it can slow the system down significantly.
                              >I am not alone with that thought.
                              >Postgres even makes an OID for each row, something you don't even see but
                              >can use if you want.
                              >
                              It's perfectly easy to disable OIDs in PostgreSQL on a case-by-case basis
                              or permanently. Most of the time, I disable OIDs.
                              >
                              On one of my current projects, I've got twelve tables, only one of which
                              has a surrogate integer primary key (technically it doesn't autonumber,
                              but I use MAX() to simulate autonumbering when creating a new record).
                              Guess which table is causing me the most problems?
                              >
                              I can imagine. Using MAX() like this can cause concurrency problems.
                              That's why RDB designers came out with the auto-numbering columns.
                              It's a table of articles. A table of comments references the articles via
                              its numeric key. Now what happens if I write an updated version of an
                              article, but want to keep a copy of the old one? I mark a status flag on
                              the old one to hide it, then create another article with the same URL but
                              a different ID number. Unfortunately the comments still point to the old
                              ID number, so are not seen when you visit the new page.
                              >
                              It's not that hard to update the comment table when updating the article
                              table.
                              Better would have been to design my table so that it had a primary key
                              like (url, revision).
                              >
                              But then your comment table wouldn't point to it anyway if you use (url,
                              revision). Or, if you just use url, your comment table would be
                              pointing at two different entries - which is not good foreign key design.
                              Anyhow, it's mostly a matter of taste. I was being somewhat tongue in
                              cheek when describing surrogate keys as "the root of all evil", but I
                              can't stand to see a good candidate key go to waste.
                              >
                              Here, I agree. But I tend to lean more towards the performance side.
                              Design the database with efficiency in mind. That's why all databases
                              aren't 5NF form.

                              --
                              =============== ===
                              Remove the "x" from my email address
                              Jerry Stuckle
                              JDS Computer Training Corp.
                              jstucklex@attgl obal.net
                              =============== ===

                              Comment

                              Working...