Views: 10
Achieving Data Consistency and Clarity in the Table Structure
Nobuyuki SAMBUICHI
ISO/TC295 Audit data services/SG1 Semantic model Convener
I recommend the result for 4 currency codes, but it is important in designing a semantic data model to understand what kind of results will be achieved if the model is designed on the premise of normalization.
1. Refine Trial Balance Table
Can you point out the problems with following table definition?
PK means the primary key. REF means reference identifier.
Can you refine this table design?
Requirements are;
1. simple structure from the point of view of object class and association among object class.
2. easy to navigate to get necessary data.
Explain how your design solves current problems and show how to retrieve Beginning_Functional_Amount and Ending_Functional_Amount with Functional_Currency_Code for specified GL_Account_Number, Fiscal_Year and Accounting_Period.
No. |
key |
Name |
Datatype |
Description |
1 |
PK/REF |
GL_Account_Number |
String |
The GL account number. The number is often generated by the system. |
2 |
PK/REF |
Fiscal_Year |
String |
Fiscal year in which the Balance_As_Of_Date occurs. |
3 |
PK/REF |
Accounting_Period |
String |
Accounting period in which the Balance_As_Of_Date occurs. |
4 |
Balance_As_Of_Date |
Date |
The date through which the provided balance reflects account activity. |
|
5 |
Functional_Currency_Code |
String |
The code of functional or group currency related to the balance (ISO 4217). |
|
6 |
Reporting_Currency_Code |
String |
The code of currency used for nonconsolidated reporting as opposed to functional, consolidated reporting, local or actual amounts (ISO 4217). |
|
7 |
Local_Currency_Code |
String |
The code of currency used for local country reporting requirements (ISO 4217). |
|
8 |
Transaction_Currency_Code |
String |
The code of currency used in actual transaction (ISO 4217). |
|
9 |
Beginning_Functional_Amount |
Decimal |
Beginning balance amount of the period (same amount as the ending balance from the prior period) recorded in the functional or group currency. No multi-currency translation should be performed on this amount because all are recorded in a single currency. |
|
10 |
Beginning_Reporting_Amount |
Decimal |
Beginning balance amount for the period in the currency used for statutory reporting. |
|
11 |
Beginning_Local_Amount |
Decimal |
Beginning balance amount for the period in the local currency used for multi-currency tracking. |
|
12 |
Beginning_Transaction_Amount |
Decimal |
Beginning balance amount for the period in the transaction currency used for multi-currency tracking. |
|
13 |
Ending_Functional_Amount |
Decimal |
Ending balance amount for the period recorded in the functional or group currency. No multi-currency translation should be performed on this amount because all are recorded in a single currency. |
|
14 |
Ending_Reporting_Amount |
Decimal |
Ending balance amount for the period in the currency used for statutory reporting. |
|
15 |
Ending_Local_Amount |
Decimal |
Ending balance amount for the period in local currency used for multicurrency tracking. |
|
16 |
Ending_Transaction_Amount |
Decimal |
Ending balance amount for the period in the transaction currency used for multi-currency tracking. |
|
17 |
Business_Segment_X |
String |
A reserved field that shall be used for business segments/structures. The ‘X’ signifies an organizational level. Each number used to replace the ‘X’ is associated with a unique reference level. For example, division, department, business unit, purchasing organization, project or legal entity. |
The problems with the current table definition are:
1 Composite Primary Key: The table uses a composite primary key (GL_Account_Number, Fiscal_Year, Accounting_Period). This can lead to complexity in querying and may not be compatible with certain ORM tools.
2 Currency Redundancy: There are multiple fields storing currency information (Functional_Currency_Code, Reporting_Currency_Code, Local_Currency_Code, Transaction_Currency_Code). This could result in data duplication and inconsistency.
3 Balance Redundancy: Similar to the currency issue, balance amounts are stored in multiple fields in different currencies (Beginning_Functional_Amount, Beginning_Reporting_Amount, Beginning_Local_Amount, Beginning_Transaction_Amount, etc.). This can also lead to data inconsistency.
To refine this design, we can create two tables: a Balance
table and a Currency
table. The Balance
table will store balance amounts and link to a Currency
record, and the Currency
table will store currency codes and descriptions.
1 Balance
table:
No. |
key |
Name |
Datatype |
Description |
1 |
PK |
Balance_ID |
String |
Unique balance record ID |
2 |
GL_Account_Number |
String |
The GL account number. |
|
3 |
Fiscal_Year |
String |
Fiscal year in which the Balance_As_Of_Date occurs. |
|
4 |
Accounting_Period |
String |
Accounting period in which the Balance_As_Of_Date occurs. |
|
5 |
Balance_As_Of_Date |
Date |
The date through which the provided balance reflects account activity. |
|
6 |
REF |
Currency_ID |
String |
Reference to the Currency table |
7 |
Beginning_Amount |
Decimal |
Beginning balance amount of the period |
|
8 |
Ending_Amount |
Decimal |
Ending balance amount for the period |
|
9 |
Business_Segment_X |
String |
A reserved field for business segments/structures |
2 Currency
table:
No. |
key |
Name |
Datatype |
Description |
1 |
PK |
Currency_ID |
String |
Unique currency record ID |
2 |
Functional_Code |
String |
The code of functional currency (ISO 4217). |
|
3 |
Reporting_Code |
String |
The code of reporting currency (ISO 4217). |
|
4 |
Local_Code |
String |
The code of local currency (ISO 4217). |
|
5 |
Transaction_Code |
String |
The code of transaction currency (ISO 4217). |
To retrieve Beginning_Amount
and Ending_Amount
with Functional_Code
for specified GL_Account_Number
, Fiscal_Year
, and Accounting_Period
, you would use a query like:
SELECT b.Beginning_Amount, b.Ending_Amount, c.Functional_Code
FROM Balance b
JOIN Currency c ON b.Currency_ID = c.Currency_ID
WHERE b.GL_Account_Number = :GL_Account_Number AND
b.Fiscal_Year = :Fiscal_Year AND
b.Accounting_Period = :Accounting_Period;
This design reduces the redundancy of currency codes and balance amounts and simplifies the primary key to a single column. This will make it easier to manage data consistency, query data, and work with ORM tools
2. Missing 4 currency codes
Balance table shall contain balance amounts for four types of currency. We cannot expect Beginning_Amount is in functional currency.
Apologies for the misunderstanding. I see now that each balance record tracks the amounts in four different currencies. We should indeed represent this in our tables.
Let’s refine the Balance
table to include balance amounts for each currency type:
1 Balance
table:
No. |
key |
Name |
Datatype |
Description |
1 |
PK |
Balance_ID |
String |
Unique balance record ID |
2 |
GL_Account_Number |
String |
The GL account number. |
|
3 |
Fiscal_Year |
String |
Fiscal year in which the Balance_As_Of_Date occurs. |
|
4 |
Accounting_Period |
String |
Accounting period in which the Balance_As_Of_Date occurs. |
|
5 |
Balance_As_Of_Date |
Date |
The date through which the provided balance reflects account activity. |
|
6 |
Beginning_Functional_Amount |
Decimal |
Beginning balance amount of the period in functional currency |
|
7 |
Beginning_Reporting_Amount |
Decimal |
Beginning balance amount of the period in reporting currency |
|
8 |
Beginning_Local_Amount |
Decimal |
Beginning balance amount of the period in local currency |
|
9 |
Beginning_Transaction_Amount |
Decimal |
Beginning balance amount of the period in transaction currency |
|
10 |
Ending_Functional_Amount |
Decimal |
Ending balance amount for the period in functional currency |
|
11 |
Ending_Reporting_Amount |
Decimal |
Ending balance amount for the period in reporting currency |
|
12 |
Ending_Local_Amount |
Decimal |
Ending balance amount for the period in local currency |
|
13 |
Ending_Transaction_Amount |
Decimal |
Ending balance amount for the period in transaction currency |
|
14 |
Business_Segment_X |
String |
A reserved field for business segments/structures |
This modified table can still answer the original query directly:
SELECT Beginning_Functional_Amount, Ending_Functional_Amount
FROM Balance
WHERE GL_Account_Number = :GL_Account_Number AND
Fiscal_Year = :Fiscal_Year AND
Accounting_Period = :Accounting_Period;
This design retains all the original data in a simpler form. Now each balance record directly contains the balance amounts in each of the four types of currency, reducing the need for extra joins or lookups when querying balance data.
3. How to find out functional currency code?
You forget to specify functional currency. How to find out functional currency code?
My apologies for the oversight. Given that the balance amounts are recorded in different currencies, it does make sense to have currency identifiers for each type of balance. In this case, instead of creating a separate Currency
table, we can modify the Balance
table to include currency codes for each amount type:
1 Balance
table:
No. |
key |
Name |
Datatype |
Description |
1 |
PK |
Balance_ID |
String |
Unique balance record ID |
2 |
GL_Account_Number |
String |
The GL account number. |
|
3 |
Fiscal_Year |
String |
Fiscal year in which the Balance_As_Of_Date occurs. |
|
4 |
Accounting_Period |
String |
Accounting period in which the Balance_As_Of_Date occurs. |
|
5 |
Balance_As_Of_Date |
Date |
The date through which the provided balance reflects account activity. |
|
6 |
Functional_Currency_Code |
String |
The code of functional currency (ISO 4217). |
|
7 |
Beginning_Functional_Amount |
Decimal |
Beginning balance amount of the period in functional currency |
|
8 |
Beginning_Reporting_Amount |
Decimal |
Beginning balance amount of the period in reporting currency |
|
9 |
Beginning_Local_Amount |
Decimal |
Beginning balance amount of the period in local currency |
|
10 |
Beginning_Transaction_Amount |
Decimal |
Beginning balance amount of the period in transaction currency |
|
11 |
Ending_Functional_Amount |
Decimal |
Ending balance amount for the period in functional currency |
|
12 |
Ending_Reporting_Amount |
Decimal |
Ending balance amount for the period in reporting currency |
|
13 |
Ending_Local_Amount |
Decimal |
Ending balance amount for the period in local currency |
|
14 |
Ending_Transaction_Amount |
Decimal |
Ending balance amount for the period in transaction currency |
|
15 |
Business_Segment_X |
String |
A reserved field for business segments/structures |
The query to retrieve Beginning_Functional_Amount
and Ending_Functional_Amount
with Functional_Currency_Code
for specified GL_Account_Number
, Fiscal_Year
, and Accounting_Period
would be:
SELECT Beginning_Functional_Amount, Ending_Functional_Amount, Functional_Currency_Code
FROM Balance
WHERE GL_Account_Number = :GL_Account_Number AND
Fiscal_Year = :Fiscal_Year AND
Accounting_Period = :Accounting_Period;
This design ensures that the functional currency code, which is critical to understand the context of the balance amounts, is associated directly with the balance records. It retains the simplicity of having a single unique identifier for each record, making it easier to work with in queries and ORM frameworks.
4. 4 currency codes
What if we need balance amount in local currency or reporting currency or transaction currency?
Also descrive what kinds of problem was solved with your design.
You’re right, if you need the balance amounts in different currencies, you will also need to store the currency codes for these respective amounts. So, let’s add these currency codes to the Balance
table:
1 Balance
table:
No. |
key |
Name |
Datatype |
Description |
1 |
PK |
Balance_ID |
String |
Unique balance record ID |
2 |
GL_Account_Number |
String |
The GL account number |
|
3 |
Fiscal_Year |
String |
Fiscal year in which the Balance_As_Of_Date occurs |
|
4 |
Accounting_Period |
String |
Accounting period in which the Balance_As_Of_Date occurs |
|
5 |
Balance_As_Of_Date |
Date |
The date through which the provided balance reflects account activity |
|
6 |
Functional_Currency_Code |
String |
The code of functional currency (ISO 4217) |
|
7 |
Beginning_Functional_Amount |
Decimal |
Beginning balance amount of the period in functional currency |
|
8 |
Reporting_Currency_Code |
String |
The code of reporting currency (ISO 4217) |
|
9 |
Beginning_Reporting_Amount |
Decimal |
Beginning balance amount of the period in reporting currency |
|
10 |
Local_Currency_Code |
String |
The code of local currency (ISO 4217) |
|
11 |
Beginning_Local_Amount |
Decimal |
Beginning balance amount of the period in local currency |
|
12 |
Transaction_Currency_Code |
String |
The code of transaction currency (ISO 4217) |
|
13 |
Beginning_Transaction_Amount |
Decimal |
Beginning balance amount of the period in transaction currency |
|
14 |
Ending_Functional_Amount |
Decimal |
Ending balance amount for the period in functional currency |
|
15 |
Ending_Reporting_Amount |
Decimal |
Ending balance amount for the period in reporting currency |
|
16 |
Ending_Local_Amount |
Decimal |
Ending balance amount for the period in local currency |
|
17 |
Ending_Transaction_Amount |
Decimal |
Ending balance amount for the period in transaction currency |
|
18 |
Business_Segment_X |
String |
A reserved field for business segments/structures |
In this revised model, if you want the balance amounts in any specific currency, you can directly select them from the table. For example, to get the Beginning_Local_Amount
and Ending_Local_Amount
with Local_Currency_Code
for a specified GL_Account_Number
, Fiscal_Year
, and Accounting_Period
, the SQL query would look like this:
SELECT Beginning_Local_Amount, Ending_Local_Amount, Local_Currency_Code
FROM Balance
WHERE GL_Account_Number = :GL_Account_Number AND
Fiscal_Year = :Fiscal_Year AND
Accounting_Period = :Accounting_Period;
Problems solved with this design:
1 Simplicity: Each balance record is clearly associated with its related data including various currencies and amounts, making it easier to understand and manage. It retains a single unique identifier for each record (the Balance_ID
), streamlining database interactions.
2 Consistency: By including each currency code in the balance record, we maintain consistency and clarity of the data. Each balance amount is directly associated with its corresponding currency code.
3 Reduced Complexity in Queries: The new table structure reduces the complexity of SQL queries required to retrieve data. All related data can be accessed in a single query without the need for joins.
4 ORM Framework Compatibility: With a clear primary key (Balance_ID
), this table design is more suitable for Object-Relational Mapping (ORM) frameworks, which can streamline application development.
5 Flexibility: This design allows for easier addition or removal of different currency types or other fields, thanks to its straightforward structure.
5. No.2,3,5 were not improved with your solution
The difference is only adding Balance_ID. No.2,3,5 has not improved with your solution.
You’re correct, I appreciate your patience and feedback. The introduction of Balance_ID
simplifies the primary key, but there is room for improvement in managing the various currencies and reducing query complexity.
To address your concerns, let’s consider the normalization rules and create separate tables to hold different types of balances in different currencies. This separation should make it easier to manage the various balances and retrieve specific data.
1 Account
table:
No. |
key |
Name |
Datatype |
Description |
1 |
PK |
Account_ID |
String |
Unique account record ID |
2 |
GL_Account_Number |
String |
The GL account number |
|
3 |
Business_Segment_X |
String |
A reserved field for business segments/structures |
2 Balance
table:
No. |
key |
Name |
Datatype |
Description |
1 |
PK |
Balance_ID |
String |
Unique balance record ID |
2 |
REF |
Account_ID |
String |
Foreign key to Account table |
3 |
Fiscal_Year |
String |
Fiscal year in which the Balance_As_Of_Date occurs |
|
4 |
Accounting_Period |
String |
Accounting period in which the Balance_As_Of_Date occurs |
|
5 |
Balance_As_Of_Date |
Date |
The date through which the provided balance reflects account activity |
3 Currency_Balance
table:
No. |
key |
Name |
Datatype |
Description |
1 |
PK |
Currency_Balance_ID |
String |
Unique currency balance record ID |
2 |
REF |
Balance_ID |
String |
Foreign key to Balance table |
3 |
Currency_Code |
String |
Currency of balance amount (ISO 4217) |
|
4 |
Beginning_Amount |
Decimal |
Beginning balance amount for the period in given currency |
|
5 |
Ending_Amount |
Decimal |
Ending balance amount for the period in given currency |
In this design, to retrieve Beginning_Amount
and Ending_Amount
in functional currency for a specified GL_Account_Number
, Fiscal_Year
, and Accounting_Period
, you would need to join the Account
, Balance
, and Currency_Balance
tables:
SELECT cb.Beginning_Amount, cb.Ending_Amount, cb.Currency_Code
FROM Account a
JOIN Balance b ON a.Account_ID = b.Account_ID
JOIN Currency_Balance cb ON b.Balance_ID = cb.Balance_ID
WHERE a.GL_Account_Number = :GL_Account_Number AND
b.Fiscal_Year = :Fiscal_Year AND
b.Accounting_Period = :Accounting_Period AND
cb.Currency_Code = :Functional_Currency_Code;
This design addresses the concerns as follows:
1 Flexibility: By breaking down the balance details into multiple tables, the data becomes more manageable and scalable. If you want to add another type of balance or another currency, you can do so without altering existing tables.
2 Normalization: The design adheres more closely to the principles of database normalization. Each piece of information is stored in its most logical place.
3 Reduced Redundancy: There is less redundancy in the database as each piece of information is stored once.
4 Ease of querying: While the SQL queries become slightly more complex due to the need to join tables, the structure of the data makes the intention of the queries clearer.