Search Postgresql Archives

Re: Problem with UPDATE and UNIQUE

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Michael Glaesemann wrote:
> 
> On Aug 22, 2007, at 1:02 , Frank Millman wrote:
> 
> > I want to store data in a 'tree' form, with a fixed number 
> of levels, 
> > so that each level has a defined role.
> 

Thanks very much for the in-depth response, Michael. Plenty for the little
grey cells to work on.

> First thought: fixed, predetermined levels, separate tables 
> for each level. If a more general approach is desired, your 
> options are generally adjacency list, nested sets, or 
> contrib/ltree. Each has their own strengths and weaknesses.
> 

I am writing a general-purpose business/accounting application. If
successful, I hope to have a number of different companies using it. I want
to provide the ability for the end-user to to define their own,
multi-dimensional, views of various core tables (general ledger, products,
etc). I foresee that it will only be used for reporting purposes
(particularly WHERE, ORDER BY and GROUP BY). Therefore I do need a general
approach.

> > I have the following (simplified) table -
> >
> > CREATE TABLE treedata (
> >   rowid serial primary key,
> >   levelno int not null,
> >   parentid int references treedata,
> >   seq int not null,
> >   code varchar not null,
> >   description varchar not null
> >   );
> 
> rowid + parentid looks like adjacency list to me. Note that 
> you're storing redundant data (the levelno, which can be 
> derived from the rowid/parentid relationships), which you may 
> want to do for performance reasons, but does make things more 
> complicated: you're essentially caching data which brings 
> with it problems of cache invalidation. In this case, you 
> need to make sure you're updating levelno whenever it needs 
> to be updated. (Which I'm sure you've already thought of.)
> 

I read up on 'adjency list' and 'nested sets', and I agree, the scheme I
have come up with is an adjency list. It had not occurred to me that levelno
is redundant, but I can see that this is so. I will have to check to see if
there are any implications if I remove it.

> > To describe each of the levels in the tree, I have the 
> following table 
> > -
> >
> > CREATE TABLE treelevels (
> >   levelno int primary key,
> >   code varchar unique not null,
> >   description varchar not null
> >   );
> 
> Having each level as its own table would make this redundant, 
> but again, that might not fit with what you're modeling.
> 
> > Typical values for this table could be -
> >   (0,'Prod','Product code')
> >   (1,'Cat','Product category')
> >   (2,'*','All products')
> 
> This makes me think you'll want to rethink your schema a bit, 
> as you're mixing different types of data: categories and 
> products. I'd at least separate this out into a products 
> table and a categories table. The categories table may in 
> fact still require some kind of tree structure, but I don't 
> think products belongs as part of it.
> 

Very good point. I will give this some serious thought.

[...]

> >
> > Say I want to insert a level between 'code' and 'category' called 
> > 'group' -
> >
> > INSERT INTO treelevels VALUES (1,'Group','Product group');
> 
> It's a good habit to *always* explicitly list your columns: 
> it's self- documenting and more robust in the face of schema changes.
> 
> > Obviously this will fail with a duplicate levelno. Therefore before 
> > the insert statement I want to do this -
> >
> > UPDATE treelevels SET levelno = (levelno+1) WHERE levelno >= 1;
> >
> > The problem is that if there are a number of levels, and 
> they are in 
> > indeterminate order, I can get duplicate level numbers while the 
> > command is being executed.
> >
> > My workaround at present is the following -
> >
> > UPDATE treelevels SET levelno = (levelno+10001) WHERE levelno >= 1; 
> > UPDATE treelevels SET levelno = (levelno-10000) WHERE levelno >= 1;
> 
> This is a general problem with nested sets and your situation 
> where you're caching the levelno, and you're work around is 
> similar to the two generally recommended solutions. One is to 
> make updates using an offset such as what you're doing, and 
> the other is to utilize negative levels. I'm keen on the 
> latter, as I feel it's a bit more
> flexible: you don't need to make sure your offset is large enough.  

I also like the idea of 'negating' the level. It is neat and effective.
Thanks for the tip, I will use it.

One trivial point. I use 'negating' quite a bit, and instead of -
    SET levelno = -1 * (levelno + 1)

I have adopted the habit of using -
    SET levelno = -(levelno + 1)

It just feels a bit neater.

[...]

> 
> Anyway, hope this gives you something to think about.
> 

It certainly does. Thanks again for all the valuable advice.

Frank


---------------------------(end of broadcast)---------------------------
TIP 5: don't forget to increase your free space map settings

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Index of Archives]     [Postgresql Jobs]     [Postgresql Admin]     [Postgresql Performance]     [Linux Clusters]     [PHP Home]     [PHP on Windows]     [Kernel Newbies]     [PHP Classes]     [PHP Books]     [PHP Databases]     [Postgresql & PHP]     [Yosemite]
  Powered by Linux