Search code examples
sqlsql-servert-sqlsqlfiddle

How to express a range over multiple columns with hierarchic relation?


I'm porting an old accounting software to SQL. Here's a sample made up chart of accounts:

Account SubAcct SubSubAcct SubSubSubAcct AccountNumber Name
1110 0 0 0 1110 Banks
1110 1 0 0 1110-1 US Banks
1110 1 1 0 1110-1-1 Bank One
1110 1 1 1 1110-1-1-1 Bank One #123456
1110 1 1 2 1110-1-1-2 Bank One #234567
1110 1 1 11 1110-1-1-11 Bank One #11223344
1110 1 2 0 1110-1-2-0 Bank Two
1110 1 2 1 1110-1-2-1 Bank Two #876543
1110 2 0 0 1110-2 Foreign Banks
1110 2 1 0 1110-2-1 Japan One #556677
1120 0 0 0 1120 Receivables
1120 1 0 0 1120-1 US Receivables
1120 1 1 0 1120-1-1 Zone One
1120 1 1 1 1120-1-1-1 Customer AAA
1120 1 1 2 1120-1-1-2 Customer BBB
1120 1 1 3 1120-1-1-3 Customer CCC
1120 1 2 0 1120-1-2-0 Zone Two
1120 1 2 1 1120-1-2-1 Customer WWW
1120 1 2 2 1120-1-2-2 Customer YYY

I need to query any range of accounts, for example, from account number 1110-1-1-2 to account number 1120-1-2.

This works:

SELECT * FROM Accounts 
WHERE FORMAT(Account,'D8')+'-'+
      FORMAT(SubAcct,'D8')+'-'+
      FORMAT(SubSubAcct,'D8')+'-'+
      FORMAT(SubSubSubAcct,'D8') 
   BETWEEN '00001110-00000001-00000001-00000002' 
   AND     '00001120-00000001-00000002-00000000'
ORDER BY Account,SubAcct,SubSubAcct,SubSubSubAcct

But I don't think it's a good way to do it. Here's a SQLFiddle with sample schema and data.

I'll appreciate any ideas on how to express the query or for a better table definition.


Solution

  • Just for completeness, here is one simple approach. The performace should be better than what you have now.

    SELECT * 
    FROM Accounts
    WHERE 
    (
      account > 1110 OR
      account = 1110 AND subacct > 1 OR
      account = 1110 AND subacct = 1 AND subsubacct > 1 OR
      account = 1110 AND subacct = 1 AND subsubacct = 1 AND subsubsubacct >= 2  
    ) AND (
      account < 1120 OR
      account = 1120 AND subacct < 1 OR
      account = 1120 AND subacct = 1 AND subsubacct < 2 OR
      account = 1120 AND subacct = 1 AND subsubacct = 2 AND subsubsubacct <= 0
    )
    

    You can add account BETWEEN 1110 AND 1120 to the condition if the optimizer fails to find appropriate range scan.