Loading learning content...
If inheritance gives subtypes everything from their supertypes, what makes subtypes different from each other? The answer lies in local attributes—the attributes defined specifically for a subtype that don't exist in the supertype or in sibling subtypes.
Local attributes are the raison d'être of specialization. Without them, there would be no structural reason to create subtypes; a simple discriminator attribute would suffice. Local attributes capture the unique characteristics that make each subtype semantically distinct and practically useful.
By the end of this page, you will master local attributes in EER specialization: how to identify them, design them effectively, handle their constraints, and understand their implications for schema design and data management. You'll develop judgment about when attributes should be local versus inherited.
Definition:
A local attribute (also called a specific attribute or subtype attribute) is an attribute that:
Formal Notation:
For subtype S with supertype E:
Example:
EMPLOYEE (supertype)
├── emp_id (PK)
├── name
├── salary
└── hire_date
MANAGER (subtype) - Local Attributes:
├── department ← Local: only managers have departments they lead
├── budget ← Local: only managers have budgets to manage
└── direct_reports ← Local: only managers have people reporting to them
ENGINEER (subtype) - Local Attributes:
├── tech_stack[] ← Local: only engineers have technical expertise
├── github_handle ← Local: only engineers have code repositories
└── certifications[] ← Local: only engineers have technical certifications
Note that these local attributes would be meaningless or inappropriate for other subtypes—a SALESPERSON doesn't have a tech_stack, and an ENGINEER doesn't have direct_reports (in their engineer capacity).
Ask: 'Does this attribute make sense for ALL subtypes of the supertype?' If YES → it belongs in the supertype (and will be inherited). If NO → it's a local attribute of the specific subtype where it makes sense.
Local attributes come in various forms, each serving different modeling purposes. Understanding these categories helps in attribute design.
| Category | Description | Example |
|---|---|---|
| Role-Specific Attributes | Attributes relevant to the role the subtype plays | Manager.team_size, Doctor.specialty, Teacher.subjects[] |
| Capability Attributes | Attributes describing what the subtype can do | PremiumMember.download_limit, AdminUser.access_scope, TruckDriver.cdl_class |
| Measurement Attributes | Type-specific metrics or quantities | SavingsAccount.interest_rate, ElectricVehicle.battery_capacity, ServerNode.cpu_cores |
| Relationship Attributes | Foreign keys to type-specific related entities | Student.advisor_id, Patient.primary_physician_id, ProjectManager.portfolio_id |
| Configuration Attributes | Type-specific settings or preferences | BusinessAccount.tax_id, InternationalShipping.customs_code, EnterpriseUser.sso_config |
| Temporal Attributes | Type-specific dates or timestamps | TenuredProfessor.tenure_date, RetiredEmployee.retirement_date, CompletedOrder.shipped_date |
| Status Attributes | Type-specific state information | PendingClaim.review_stage, ActiveSubscription.renewal_date, GraduatedStudent.graduation_honors |
Composite Local Attributes:
Local attributes can be composite, just like any other attribute:
BUSINESS_CUSTOMER (subtype of CUSTOMER)
└── billing_address (composite local attribute)
├── company_name
├── attention_line
├── street
├── city
├── state
├── postal_code
└── country
While CUSTOMER might have an address attribute (inherited), BUSINESS_CUSTOMER has a specialized billing_address with business-specific components like company_name and attention_line.
Multivalued Local Attributes:
Local attributes can also be multivalued:
PHYSICIAN (subtype of MEDICAL_STAFF)
├── board_certifications[] ← Multivalued: can hold multiple certification records
├── hospital_privileges[] ← Multivalued: privileges at multiple hospitals
└── specialty_areas[] ← Multivalued: multiple areas of specialty
Well-designed local attributes enhance model clarity, support efficient queries, and enable proper constraint enforcement. Here are principles and practices for effective local attribute design.
Principle 1: Semantic Appropriateness
Local attributes must genuinely belong to that specific subtype. Ask:
Principle 2: Constraint-Aware Design
Consider what constraints the local attribute requires:
-- Local attribute with constraints
CREATE TABLE SAVINGS_ACCOUNT (
account_id INT PRIMARY KEY REFERENCES ACCOUNT(account_id),
-- Local attributes with constraints
interest_rate DECIMAL(5,4) NOT NULL CHECK (interest_rate >= 0 AND interest_rate <= 0.10),
min_balance DECIMAL(10,2) DEFAULT 100.00,
compound_frequency ENUM('DAILY', 'MONTHLY', 'QUARTERLY', 'ANNUALLY') NOT NULL,
withdrawal_limit INT CHECK (withdrawal_limit >= 0)
);
Principle 3: Nullability Decisions
For each local attribute, explicitly decide:
MANAGER
├── department (NOT NULL) — Every manager heads a department
├── budget (NOT NULL) — Every manager has a budget
├── secretary_id (NULL) — Not all managers have secretaries
└── company_car_id (NULL) — Only some managers get company cars
A common error is adding attributes to subtypes that only apply to SOME instances of that subtype. If only SOME managers have company cars, either make it nullable, or consider whether there's a further subtype (EXECUTIVE_MANAGER) that always has the attribute. Don't create attributes that are mostly NULL even within the subtype.
A particularly important category of local attributes are foreign keys that establish subtype-specific relationships. These are relationships that only the subtype participates in—not the supertype or siblings.
Example: University System
In this model:
These relationships cannot be modeled on PERSON because they're semantically inappropriate for other subtypes. A faculty member doesn't have an 'advisor' in the student sense; a student doesn't have an 'office assignment' in the staff sense.
Subtype-Specific Relationship vs. Inherited Relationship:
| Question | If YES... | If NO... |
|---|---|---|
| Does this relationship apply to ALL subtypes? | Place on supertype (inherited) | Place on specific subtype (local) |
| Would sibling subtypes make sense with this relationship? | Consider supertype placement | Definitely local to this subtype |
| Does the relationship target change by subtype? | Use local relationships per subtype | Can use supertype relationship |
| Are relationship constraints different by subtype? | May need local with specific constraints | Supertype with inherited constraints |
Name local relationship attributes to reflect the subtype's perspective: Student.advisor_id (not Student.person_id), Faculty.department_id (not Faculty.unit_id). The name should convey the semantic meaning in the context of that subtype.
Local attributes can have their own constraints, independent of any inherited constraints. These constraints are enforced only for the subtype where the local attribute is defined.
Types of Local Attribute Constraints:
12345678910111213141516171819202122232425262728293031323334353637383940
-- Local Attribute Constraints Examples -- SAVINGS_ACCOUNT local constraintsCREATE TABLE SAVINGS_ACCOUNT ( account_id INT PRIMARY KEY REFERENCES ACCOUNT(account_id), -- Domain constraint: interest rate between 0 and 10% interest_rate DECIMAL(5,4) NOT NULL CHECK (interest_rate >= 0 AND interest_rate <= 0.10), -- NOT NULL constraint: compound frequency is required compound_frequency VARCHAR(20) NOT NULL CHECK (compound_frequency IN ('DAILY', 'MONTHLY', 'QUARTERLY', 'ANNUALLY')), -- Range constraint: withdrawal limit must be positive monthly_withdrawal_limit INT CHECK (monthly_withdrawal_limit > 0), -- Default value: minimum balance defaults to 100 minimum_balance DECIMAL(10,2) NOT NULL DEFAULT 100.00); -- INVESTMENT_ACCOUNT local constraintsCREATE TABLE INVESTMENT_ACCOUNT ( account_id INT PRIMARY KEY REFERENCES ACCOUNT(account_id), -- Enum constraint: risk levels risk_level VARCHAR(20) NOT NULL CHECK (risk_level IN ('CONSERVATIVE', 'MODERATE', 'AGGRESSIVE')), -- Referential constraint: portfolio must exist portfolio_id INT NOT NULL REFERENCES PORTFOLIO(portfolio_id), -- Business rule constraint: contribution limits annual_contribution_limit DECIMAL(12,2), ytd_contributions DECIMAL(12,2) DEFAULT 0, -- Cross-attribute constraint: can't exceed limit CONSTRAINT check_contribution_limit CHECK (ytd_contributions <= annual_contribution_limit OR annual_contribution_limit IS NULL));Cross-Attribute Constraints:
Local attributes can participate in constraints involving inherited attributes:
-- PREMIUM_ACCOUNT: local attribute constrained by inherited attribute
CREATE TABLE PREMIUM_ACCOUNT (
account_id INT PRIMARY KEY REFERENCES ACCOUNT(account_id),
-- Local: premium tier based on balance
premium_tier VARCHAR(20) NOT NULL,
-- Constraint involving inherited 'balance' and local 'premium_tier'
-- (This would be enforced via trigger or application logic
-- since balance is in the ACCOUNT table)
CONSTRAINT tier_balance_consistency CHECK (
(premium_tier = 'GOLD' AND balance >= 50000) OR
(premium_tier = 'PLATINUM' AND balance >= 250000) OR
(premium_tier = 'BLACK' AND balance >= 1000000)
)
-- Note: Actual implementation depends on physical schema
);
When local attributes need to be constrained against inherited attributes that live in a different physical table (in class table inheritance), standard CHECK constraints may not work. You'll need triggers, application logic, or materialized views to enforce such constraints.
Local attributes are only accessible when querying the specific subtype where they're defined. This has important implications for query construction.
Access Patterns:
12345678910111213141516171819202122232425262728293031323334353637
-- Query 1: Accessing local attributes directly from subtypeSELECT account_id, balance, -- inherited from ACCOUNT interest_rate, -- local to SAVINGS_ACCOUNT compound_frequency -- local to SAVINGS_ACCOUNTFROM SAVINGS_ACCOUNTWHERE interest_rate > 0.03; -- Query 2: Cannot access local attributes from supertypeSELECT account_id, balance, interest_rate -- ERROR! interest_rate not in ACCOUNTFROM ACCOUNT; -- Query 3: Joining subtype to get local + inherited-- (In class table inheritance schema)SELECT a.account_id, a.balance, -- from supertype table a.holder_id, -- from supertype table s.interest_rate, -- from subtype table s.minimum_balance -- from subtype tableFROM ACCOUNT aJOIN SAVINGS_ACCOUNT s ON a.account_id = s.account_idWHERE s.interest_rate > 0.03; -- Query 4: Polymorphic query with optional local data-- (Getting all accounts, with savings-specific data when applicable)SELECT a.account_id, a.balance, a.account_type, s.interest_rate -- NULL for non-savings accountsFROM ACCOUNT aLEFT JOIN SAVINGS_ACCOUNT s ON a.account_id = s.account_id;Query Design Patterns for Local Attributes:
The actual SQL for accessing local attributes depends entirely on how the EER model was mapped to relational tables. Single table inheritance gives simpler queries but with NULLs. Class table inheritance gives cleaner data but requires JOINs. Always understand your physical schema before writing queries.
Let's examine a comprehensive example that demonstrates inherited and local attributes working together in a realistic domain.
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071
-- Complete Attribute Analysis -- PRODUCT (Supertype - Level 0)-- All attributes here are INHERITED by all subtypesCREATE TABLE PRODUCT ( product_id SERIAL PRIMARY KEY, name VARCHAR(255) NOT NULL, description TEXT, price DECIMAL(10,2) NOT NULL CHECK (price > 0), category_id INT REFERENCES CATEGORY(id), stock_qty INT DEFAULT 0 CHECK (stock_qty >= 0), created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP); -- PHYSICAL_PRODUCT (Subtype - Level 1)-- Inherits: product_id, name, description, price, category_id, stock_qty, created_at-- Local: weight_kg, dimensions, shipping_class, warehouse_idCREATE TABLE PHYSICAL_PRODUCT ( product_id INT PRIMARY KEY REFERENCES PRODUCT(product_id), weight_kg DECIMAL(8,3) NOT NULL, dimensions JSONB, -- {length, width, height} shipping_class VARCHAR(50) NOT NULL, warehouse_id INT REFERENCES WAREHOUSE(id)); -- DIGITAL_PRODUCT (Subtype - Level 1)-- Inherits: product_id, name, description, price, category_id, stock_qty, created_at-- Local: file_size_mb, download_url, format, license_typeCREATE TABLE DIGITAL_PRODUCT ( product_id INT PRIMARY KEY REFERENCES PRODUCT(product_id), file_size_mb DECIMAL(10,2), download_url VARCHAR(500) NOT NULL, format VARCHAR(50), license_type VARCHAR(50) NOT NULL CHECK (license_type IN ('PERSONAL', 'COMMERCIAL', 'ENTERPRISE'))); -- SUBSCRIPTION_PRODUCT (Subtype - Level 1)-- Inherits: product_id, name, description, price, category_id, stock_qty, created_at-- Local: billing_cycle, trial_days, auto_renew, tier_levelCREATE TABLE SUBSCRIPTION_PRODUCT ( product_id INT PRIMARY KEY REFERENCES PRODUCT(product_id), billing_cycle VARCHAR(20) NOT NULL CHECK (billing_cycle IN ('MONTHLY', 'QUARTERLY', 'ANNUAL')), trial_days INT DEFAULT 0, auto_renew BOOLEAN DEFAULT true, tier_level VARCHAR(50)); -- BOOK (Subtype of PHYSICAL_PRODUCT - Level 2)-- Inherits: product_id, name, description, price, category_id, stock_qty, created_at (from PRODUCT)-- weight_kg, dimensions, shipping_class, warehouse_id (from PHYSICAL_PRODUCT) -- Local: isbn, author, publisher, page_countCREATE TABLE BOOK ( product_id INT PRIMARY KEY REFERENCES PHYSICAL_PRODUCT(product_id), isbn VARCHAR(17) UNIQUE NOT NULL, author VARCHAR(255) NOT NULL, publisher VARCHAR(255), page_count INT CHECK (page_count > 0)); -- Query: Find all books with their complete attribute setSELECT -- From PRODUCT (inherited through chain) p.product_id, p.name, p.description, p.price, p.category_id, p.stock_qty, -- From PHYSICAL_PRODUCT (inherited) pp.weight_kg, pp.shipping_class, pp.warehouse_id, -- From BOOK (local) b.isbn, b.author, b.publisher, b.page_countFROM PRODUCT pJOIN PHYSICAL_PRODUCT pp ON p.product_id = pp.product_idJOIN BOOK b ON pp.product_id = b.product_idWHERE b.author LIKE '%Knuth%';This page has provided comprehensive coverage of local attributes in EER specialization. Let's consolidate the essential knowledge:
What's Next:
With our understanding of both inherited and local attributes complete, we now turn to notation—the visual language for representing specialization hierarchies in EER diagrams, including standard symbols for supertypes, subtypes, inheritance, constraints, and discriminators.
You now have a complete understanding of local attributes—how to identify them, design them, constrain them, and query them. Combined with your knowledge of inheritance, you can fully specify the attribute structure of any specialization hierarchy. Next, we'll learn to express these concepts visually.