@memberjunction/db-auto-doc 5.14.0 → 5.15.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +169 -29
- package/bin/run.js +1 -1
- package/dist/commands/analyze.d.ts +3 -0
- package/dist/commands/analyze.d.ts.map +1 -1
- package/dist/commands/analyze.js +33 -3
- package/dist/commands/analyze.js.map +1 -1
- package/dist/commands/prune.d.ts +17 -0
- package/dist/commands/prune.d.ts.map +1 -0
- package/dist/commands/prune.js +153 -0
- package/dist/commands/prune.js.map +1 -0
- package/dist/core/AnalysisEngine.d.ts +44 -0
- package/dist/core/AnalysisEngine.d.ts.map +1 -1
- package/dist/core/AnalysisEngine.js +427 -1
- package/dist/core/AnalysisEngine.js.map +1 -1
- package/dist/core/AnalysisOrchestrator.d.ts.map +1 -1
- package/dist/core/AnalysisOrchestrator.js +33 -10
- package/dist/core/AnalysisOrchestrator.js.map +1 -1
- package/dist/discovery/FKDetector.d.ts +6 -0
- package/dist/discovery/FKDetector.d.ts.map +1 -1
- package/dist/discovery/FKDetector.js +101 -4
- package/dist/discovery/FKDetector.js.map +1 -1
- package/dist/discovery/PKDetector.d.ts +7 -0
- package/dist/discovery/PKDetector.d.ts.map +1 -1
- package/dist/discovery/PKDetector.js +121 -6
- package/dist/discovery/PKDetector.js.map +1 -1
- package/dist/drivers/MySQLDriver.d.ts.map +1 -1
- package/dist/drivers/MySQLDriver.js +2 -0
- package/dist/drivers/MySQLDriver.js.map +1 -1
- package/dist/drivers/PostgreSQLDriver.d.ts.map +1 -1
- package/dist/drivers/PostgreSQLDriver.js +2 -0
- package/dist/drivers/PostgreSQLDriver.js.map +1 -1
- package/dist/drivers/SQLServerDriver.d.ts.map +1 -1
- package/dist/drivers/SQLServerDriver.js +2 -0
- package/dist/drivers/SQLServerDriver.js.map +1 -1
- package/dist/prompts/PromptEngine.d.ts +19 -0
- package/dist/prompts/PromptEngine.d.ts.map +1 -1
- package/dist/prompts/PromptEngine.js +91 -7
- package/dist/prompts/PromptEngine.js.map +1 -1
- package/dist/types/analysis.d.ts +10 -0
- package/dist/types/analysis.d.ts.map +1 -1
- package/dist/types/config.d.ts +47 -0
- package/dist/types/config.d.ts.map +1 -1
- package/dist/types/config.js.map +1 -1
- package/dist/types/prompts.d.ts +26 -0
- package/dist/types/prompts.d.ts.map +1 -1
- package/dist/utils/config-loader.js +2 -2
- package/dist/utils/config-loader.js.map +1 -1
- package/dist/utils/ensureArray.d.ts +13 -0
- package/dist/utils/ensureArray.d.ts.map +1 -0
- package/dist/utils/ensureArray.js +39 -0
- package/dist/utils/ensureArray.js.map +1 -0
- package/package.json +5 -5
- package/prompts/fk-evaluation.md +94 -0
- package/prompts/fk-pruning-holistic.md +57 -0
- package/prompts/fk-pruning-table.md +51 -0
- package/prompts/pk-pruning-holistic.md +26 -0
- package/prompts/pk-pruning-table.md +35 -0
- package/prompts/table-analysis.md +28 -3
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@memberjunction/db-auto-doc",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "5.
|
|
4
|
+
"version": "5.15.0",
|
|
5
5
|
"description": "AI-powered database documentation generator for SQL Server, MySQL, and PostgreSQL. Analyzes your database structure, uses AI to generate comprehensive descriptions, and saves them as metadata. Works standalone - no MemberJunction runtime required.",
|
|
6
6
|
"main": "dist/index.js",
|
|
7
7
|
"types": "dist/index.d.ts",
|
|
@@ -72,10 +72,10 @@
|
|
|
72
72
|
},
|
|
73
73
|
"dependencies": {
|
|
74
74
|
"@inquirer/prompts": "^8.2.0",
|
|
75
|
-
"@memberjunction/ai": "5.
|
|
76
|
-
"@memberjunction/server-bootstrap": "5.
|
|
77
|
-
"@memberjunction/core": "5.
|
|
78
|
-
"@memberjunction/global": "5.
|
|
75
|
+
"@memberjunction/ai": "5.15.0",
|
|
76
|
+
"@memberjunction/server-bootstrap": "5.15.0",
|
|
77
|
+
"@memberjunction/core": "5.15.0",
|
|
78
|
+
"@memberjunction/global": "5.15.0",
|
|
79
79
|
"@oclif/core": "^3.27.0",
|
|
80
80
|
"@oclif/plugin-help": "^6.2.37",
|
|
81
81
|
"chalk": "^5.6.2",
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
You are a database expert evaluating foreign key candidates that were identified by statistical analysis. Your job is to confirm or reject each candidate based on semantic plausibility, directionality, and database design principles.
|
|
2
|
+
|
|
3
|
+
## Database Context
|
|
4
|
+
{% if seedContext %}
|
|
5
|
+
{% if seedContext.overallPurpose %}- **Purpose**: {{ seedContext.overallPurpose }}{% endif %}
|
|
6
|
+
{% if seedContext.businessDomains %}- **Business Domains**: {{ seedContext.businessDomains | join(', ') }}{% endif %}
|
|
7
|
+
{% if seedContext.industryContext %}- **Industry**: {{ seedContext.industryContext }}{% endif %}
|
|
8
|
+
{% endif %}
|
|
9
|
+
|
|
10
|
+
## All Tables
|
|
11
|
+
{% for tbl in allTables %}
|
|
12
|
+
- **{{ tbl.schema }}.{{ tbl.name }}**{% if tbl.description %}: {{ tbl.description }}{% endif %}{% if tbl.pk %} (PK: {{ tbl.pk }}){% endif %}
|
|
13
|
+
{% endfor %}
|
|
14
|
+
|
|
15
|
+
## FK Candidates to Evaluate
|
|
16
|
+
The following candidates were found by statistical analysis (value overlap, naming patterns, cardinality). Each has statistical evidence but needs semantic validation.
|
|
17
|
+
|
|
18
|
+
{% for fk in candidates %}
|
|
19
|
+
{{ loop.index }}. **{{ fk.sourceSchema }}.{{ fk.sourceTable }}.{{ fk.sourceColumn }}** → **{{ fk.targetSchema }}.{{ fk.targetTable }}.{{ fk.targetColumn }}**
|
|
20
|
+
- Statistical confidence: {{ fk.confidence }}%
|
|
21
|
+
- Value overlap: {{ (fk.valueOverlap * 100) | round(1) }}%
|
|
22
|
+
- Source nulls: {{ (fk.nullPercentage * 100) | round(1) }}%
|
|
23
|
+
- Cardinality ratio (source/target distinct): {{ fk.cardinalityRatio | round(2) }}
|
|
24
|
+
{% endfor %}
|
|
25
|
+
|
|
26
|
+
## Evaluation Rules
|
|
27
|
+
|
|
28
|
+
### Rule 1: Value overlap is the strongest signal — respect it
|
|
29
|
+
High value overlap (>85%) means the source column's values are almost entirely contained within the target column's values. This is near-proof of a FK relationship. **Only reject high-overlap candidates if you have a concrete, specific reason** (e.g., clearly wrong direction, or an obviously better target exists for the same source column).
|
|
30
|
+
|
|
31
|
+
Do NOT reject candidates just because column names don't match. Real databases frequently use alias names for FK columns:
|
|
32
|
+
- `PersonID` referencing `BusinessEntityID` (alias for the same concept)
|
|
33
|
+
- `ComponentID` referencing `ProductID` (a component IS a product)
|
|
34
|
+
- `Owner` referencing `BusinessEntityID` (semantic alias)
|
|
35
|
+
- `SizeUnitMeasureCode` referencing `UnitMeasureCode` (prefixed FK)
|
|
36
|
+
- `FromCurrencyCode` / `ToCurrencyCode` referencing `CurrencyCode` (role-based aliases)
|
|
37
|
+
|
|
38
|
+
These are all valid FKs despite name mismatches. The statistical overlap proves the relationship.
|
|
39
|
+
|
|
40
|
+
### Rule 2: Directionality — child references parent
|
|
41
|
+
FKs point FROM the child table TO the parent table. The child table contains the FK column referencing the parent's PK/unique key.
|
|
42
|
+
- `OrderDetail.ProductID → Product.ProductID` is CORRECT (child → parent, many-to-one)
|
|
43
|
+
- `Product.ProductID → OrderDetail.ProductID` is WRONG (parent → child, one-to-many)
|
|
44
|
+
|
|
45
|
+
**How to determine direction**: The target table should generally have FEWER or EQUAL distinct values in the referenced column compared to the source. A cardinality ratio > 1.0 suggests correct child→parent direction.
|
|
46
|
+
|
|
47
|
+
### Rule 3: Inheritance / specialization — prefer the most specific target
|
|
48
|
+
When a source column has candidates pointing to multiple target tables (e.g., `BusinessEntityID` exists in both `BusinessEntity` and `Person`), the correct FK is usually to the **most specialized table**, not the root/base table. This is the Table-Per-Type inheritance pattern common in databases:
|
|
49
|
+
- `Employee.BusinessEntityID → Person.BusinessEntityID` is CORRECT (Employee IS-A Person)
|
|
50
|
+
- `Employee.BusinessEntityID → BusinessEntity.BusinessEntityID` is WRONG (too generic — Employee relates to Person specifically)
|
|
51
|
+
|
|
52
|
+
Look at the table names and relationships to identify inheritance chains. The FK should point to the table that the source table has the most specific relationship with.
|
|
53
|
+
|
|
54
|
+
### Rule 4: Transitive hops — reject indirect relationships
|
|
55
|
+
If `A.col` and `B.col` both reference the same parent table but A and B have no direct relationship, reject `A.col → B.col`. Example:
|
|
56
|
+
- `EmployeePayHistory.BusinessEntityID → PersonPhone.BusinessEntityID` — REJECT (both reference Person independently; PayHistory doesn't depend on PersonPhone)
|
|
57
|
+
|
|
58
|
+
**Key distinction**: A transitive hop has LOW cardinality ratio (close to 1.0) and makes no business sense. A real FK has a meaningful dependency.
|
|
59
|
+
|
|
60
|
+
### Rule 5: Semantic plausibility
|
|
61
|
+
Does the relationship make business sense? Consider the table purposes. But remember: statistical evidence (high value overlap) outweighs naming concerns. If the data proves the relationship, confirm it even if the naming seems unusual.
|
|
62
|
+
|
|
63
|
+
### Rule 6: Multiple candidates for same source column
|
|
64
|
+
When a source column has multiple FK candidates, you may confirm MORE THAN ONE if they are genuinely valid (e.g., a column that references different tables in different contexts). But typically, prefer the single best target and reject the others.
|
|
65
|
+
|
|
66
|
+
## Response Format
|
|
67
|
+
|
|
68
|
+
Return a JSON array where each object represents your evaluation of ONE candidate. Use the same index as the input list. Only include candidates you are confirming — omit rejected ones entirely.
|
|
69
|
+
|
|
70
|
+
```json
|
|
71
|
+
[
|
|
72
|
+
{
|
|
73
|
+
"index": 1,
|
|
74
|
+
"verdict": "confirm",
|
|
75
|
+
"confidence": 0.95,
|
|
76
|
+
"reasoning": "Brief explanation"
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
"index": 3,
|
|
80
|
+
"verdict": "confirm",
|
|
81
|
+
"confidence": 0.80,
|
|
82
|
+
"reasoning": "Brief explanation"
|
|
83
|
+
}
|
|
84
|
+
]
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
- **index**: The 1-based index from the candidate list above
|
|
88
|
+
- **verdict**: Always "confirm" (omit rejected candidates entirely)
|
|
89
|
+
- **confidence**: Your adjusted confidence (0-1 scale)
|
|
90
|
+
- **reasoning**: Brief explanation of why this is a valid FK
|
|
91
|
+
|
|
92
|
+
**IMPORTANT**: Err on the side of confirming when statistical evidence is strong. It is better to include a borderline FK than to miss a real one. Only reject when you are confident the relationship is wrong.
|
|
93
|
+
|
|
94
|
+
Return ONLY valid JSON. Do not include markdown code fences or explanatory text.
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
You are a database expert making final decisions on proposed FK removals. A per-table analysis has proposed removing certain foreign keys. Your job is to review ALL proposed removals holistically and make final keep/remove decisions.
|
|
2
|
+
|
|
3
|
+
## Database Context
|
|
4
|
+
{% if seedContext %}
|
|
5
|
+
{% if seedContext.overallPurpose %}- **Purpose**: {{ seedContext.overallPurpose }}{% endif %}
|
|
6
|
+
{% if seedContext.businessDomains %}- **Business Domains**: {{ seedContext.businessDomains | join(', ') }}{% endif %}
|
|
7
|
+
{% if seedContext.industryContext %}- **Industry**: {{ seedContext.industryContext }}{% endif %}
|
|
8
|
+
{% endif %}
|
|
9
|
+
|
|
10
|
+
## All Database Tables
|
|
11
|
+
{% for tbl in allTables %}
|
|
12
|
+
- **{{ tbl.schema }}.{{ tbl.name }}**{% if tbl.pk %} (PK: {{ tbl.pk }}){% endif %}{% if tbl.description %}: {{ tbl.description }}{% endif %}
|
|
13
|
+
{% endfor %}
|
|
14
|
+
|
|
15
|
+
## Proposed FK Removals
|
|
16
|
+
The per-table analysis proposed removing these FKs. Review each one and decide whether to confirm the removal or keep the FK.
|
|
17
|
+
|
|
18
|
+
{% for proposal in proposals %}
|
|
19
|
+
{{ loop.index }}. **{{ proposal.sourceSchema }}.{{ proposal.sourceTable }}.{{ proposal.sourceColumn }}** → **{{ proposal.targetSchema }}.{{ proposal.targetTable }}.{{ proposal.targetColumn }}** (confidence: {{ proposal.confidence }}%)
|
|
20
|
+
- **Removal reason**: {{ proposal.reasoning }}
|
|
21
|
+
{% endfor %}
|
|
22
|
+
|
|
23
|
+
## Review Guidelines
|
|
24
|
+
|
|
25
|
+
Consider the FULL relationship graph when making decisions:
|
|
26
|
+
- If removing an FK would leave a table with NO outgoing relationships, reconsider — most tables have at least one FK
|
|
27
|
+
- If the per-table pass proposed removing an FK because a "better" target exists, verify that the better target FK actually exists in the confirmed set
|
|
28
|
+
- If multiple tables have the same column pointing to the same target and the per-table pass wants to remove only some, consider consistency
|
|
29
|
+
- Reverse-direction FKs (parent→child) should almost always be removed
|
|
30
|
+
- Transitive hops (A→B when both reference C independently) should almost always be removed
|
|
31
|
+
|
|
32
|
+
## Response Format
|
|
33
|
+
|
|
34
|
+
Return a JSON array with your final decision for EACH proposed removal:
|
|
35
|
+
|
|
36
|
+
```json
|
|
37
|
+
[
|
|
38
|
+
{
|
|
39
|
+
"index": 1,
|
|
40
|
+
"action": "remove",
|
|
41
|
+
"reasoning": "Confirmed — reverse direction FK, Department is the parent"
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
"index": 3,
|
|
45
|
+
"action": "keep",
|
|
46
|
+
"reasoning": "On reflection, this is a valid FK — the per-table analysis missed that these tables are directly related"
|
|
47
|
+
}
|
|
48
|
+
]
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
- **index**: The 1-based index from the proposed removals list above
|
|
52
|
+
- **action**: `"remove"` to confirm removal, `"keep"` to override and keep the FK
|
|
53
|
+
- **reasoning**: Brief explanation
|
|
54
|
+
|
|
55
|
+
**Every proposed removal must have a decision.** Do not omit any.
|
|
56
|
+
|
|
57
|
+
Return ONLY valid JSON. Do not include markdown code fences or explanatory text.
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
You are a database expert reviewing foreign key relationships for a single table. Your job is to identify ONLY the clearly incorrect FKs that should be removed.
|
|
2
|
+
|
|
3
|
+
## Table: {{ sourceSchema }}.{{ sourceTable }}
|
|
4
|
+
{% if tableDescription %}**Description**: {{ tableDescription }}{% endif %}
|
|
5
|
+
|
|
6
|
+
## All Database Tables (for reference)
|
|
7
|
+
{% for tbl in allTables %}
|
|
8
|
+
- **{{ tbl.schema }}.{{ tbl.name }}**{% if tbl.pk %} (PK: {{ tbl.pk }}){% endif %}{% if tbl.description %}: {{ tbl.description }}{% endif %}
|
|
9
|
+
{% endfor %}
|
|
10
|
+
|
|
11
|
+
## Foreign Keys to Review
|
|
12
|
+
These FKs were identified by statistical analysis and/or LLM analysis. Some are correct, some may be false positives.
|
|
13
|
+
|
|
14
|
+
**NOTE**: FKs marked as **[LOCKED]** have very high confidence and CANNOT be removed. Only evaluate the unlocked ones.
|
|
15
|
+
|
|
16
|
+
{% for fk in candidates %}
|
|
17
|
+
{{ loop.index }}. {% if fk.locked %}**[LOCKED]** {% endif %}**{{ fk.sourceColumn }}** → {{ fk.targetSchema }}.{{ fk.targetTable }}.{{ fk.targetColumn }} (confidence: {{ fk.confidence }}%)
|
|
18
|
+
{% endfor %}
|
|
19
|
+
|
|
20
|
+
## What to look for when proposing removals:
|
|
21
|
+
|
|
22
|
+
1. **Reverse direction**: FK goes from parent→child instead of child→parent. Example: `Department.DepartmentID → EmployeeDepartmentHistory.DepartmentID` is backwards — the history table should reference Department, not the other way around.
|
|
23
|
+
|
|
24
|
+
2. **Transitive/indirect relationships**: Two tables both reference a common parent but aren't directly related. Example: `EmployeePayHistory.BusinessEntityID → PersonPhone.BusinessEntityID` — both reference Person, but PayHistory doesn't depend on PersonPhone.
|
|
25
|
+
|
|
26
|
+
3. **Wrong target when better target exists**: If a column points to a generic table but a more specific table is the correct target. Example: `Employee.BusinessEntityID → BusinessEntity.BusinessEntityID` when `Employee.BusinessEntityID → Person.BusinessEntityID` is the correct FK (Person is more specific).
|
|
27
|
+
|
|
28
|
+
4. **Column name mismatch creating false match**: Same data type and overlapping values but no real relationship. Example: `OrderQty → OnOrderQty` — both are integers with overlapping ranges but aren't referential.
|
|
29
|
+
|
|
30
|
+
5. **Sibling fan-out**: When a source column has multiple FK targets with the same column name, usually only ONE is the correct FK (to the parent/lookup table). The others are sibling tables that independently reference the same parent. Look for the pattern: `A.TerritoryID → SalesTerritory.TerritoryID` (correct — SalesTerritory is the lookup) vs `A.TerritoryID → SalesTerritoryHistory.TerritoryID` (wrong — History is a sibling, not a parent). The correct target is typically:
|
|
31
|
+
- The table whose PK matches the FK column
|
|
32
|
+
- The shorter/simpler table name (lookup/master vs history/detail)
|
|
33
|
+
- The table with fewer columns (lookup tables are small)
|
|
34
|
+
|
|
35
|
+
## Response Format
|
|
36
|
+
|
|
37
|
+
Return a JSON array of FKs you propose to REMOVE. Only include FKs you are confident are wrong. Do NOT include locked FKs. If all unlocked FKs look correct, return an empty array `[]`.
|
|
38
|
+
|
|
39
|
+
```json
|
|
40
|
+
[
|
|
41
|
+
{
|
|
42
|
+
"index": 2,
|
|
43
|
+
"action": "remove",
|
|
44
|
+
"reasoning": "Reverse direction — Department is the parent table, not the child"
|
|
45
|
+
}
|
|
46
|
+
]
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
**Be moderately aggressive** — remove FKs that follow the sibling/reverse/transitive patterns described above. The locked FKs protect the high-confidence correct relationships, so your job is to clean up the lower-confidence noise.
|
|
50
|
+
|
|
51
|
+
Return ONLY valid JSON. Do not include markdown code fences or explanatory text.
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
You are performing a final review of proposed primary key removals across a database.
|
|
2
|
+
|
|
3
|
+
## Proposed PK Removals:
|
|
4
|
+
{% for p in proposals %}
|
|
5
|
+
{{ loop.index }}. {{ p.sourceSchema }}.{{ p.sourceTable }}: columns [{{ p.columns | join(", ") }}] (confidence: {{ p.confidence }}%)
|
|
6
|
+
Reasoning: {{ p.reasoning }}
|
|
7
|
+
{% endfor %}
|
|
8
|
+
|
|
9
|
+
## All Database Tables:
|
|
10
|
+
{% for tbl in allTables %}
|
|
11
|
+
- {{ tbl.schema }}.{{ tbl.name }}{% if tbl.pk %} (PK: {{ tbl.pk }}){% endif %}{% if tbl.description %} — {{ tbl.description }}{% endif %}
|
|
12
|
+
{% endfor %}
|
|
13
|
+
|
|
14
|
+
## Your Task:
|
|
15
|
+
Review each proposed removal. Consider:
|
|
16
|
+
1. Would removing this PK leave the table without any primary key?
|
|
17
|
+
2. Is this PK actually correct and should not be removed?
|
|
18
|
+
3. Are there any cross-table consistency issues?
|
|
19
|
+
|
|
20
|
+
For each proposal, confirm or reject the removal:
|
|
21
|
+
[
|
|
22
|
+
{ "index": 1, "action": "remove", "reasoning": "Confirmed: not the real PK" },
|
|
23
|
+
{ "index": 2, "action": "keep", "reasoning": "Actually correct, do not remove" }
|
|
24
|
+
]
|
|
25
|
+
|
|
26
|
+
Return ONLY valid JSON. Do not include markdown code fences or explanatory text.
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
You are evaluating primary key candidates for a database table.
|
|
2
|
+
|
|
3
|
+
## Table: {{ sourceSchema }}.{{ sourceTable }}
|
|
4
|
+
{{ tableDescription }}
|
|
5
|
+
|
|
6
|
+
## PK Candidates (evaluate these):
|
|
7
|
+
{% for pk in candidates %}
|
|
8
|
+
{{ loop.index }}. Columns: {{ pk.columns | join(", ") }} (confidence: {{ pk.confidence }}%{% if pk.locked %}, LOCKED - do not modify{% endif %})
|
|
9
|
+
{% endfor %}
|
|
10
|
+
|
|
11
|
+
## All Database Tables (for context):
|
|
12
|
+
{% for tbl in allTables %}
|
|
13
|
+
- {{ tbl.schema }}.{{ tbl.name }}{% if tbl.pk %} (PK: {{ tbl.pk }}){% endif %}
|
|
14
|
+
{% endfor %}
|
|
15
|
+
|
|
16
|
+
## Your Task:
|
|
17
|
+
Evaluate each UNLOCKED PK candidate. A valid primary key must:
|
|
18
|
+
1. Uniquely identify every row in the table
|
|
19
|
+
2. Be the most natural identifier for the entity (prefer table-specific IDs over generic ones)
|
|
20
|
+
3. For junction/bridge tables, be the combination of the foreign key columns
|
|
21
|
+
4. Only ONE primary key should exist per table
|
|
22
|
+
|
|
23
|
+
For each candidate, respond with:
|
|
24
|
+
- `"action": "keep"` or `"action": "remove"`
|
|
25
|
+
- `"reasoning": "why this is/isnt the correct PK"`
|
|
26
|
+
|
|
27
|
+
If multiple candidates exist for a table, only one should be kept.
|
|
28
|
+
|
|
29
|
+
Return a JSON array:
|
|
30
|
+
[
|
|
31
|
+
{ "index": 1, "action": "keep", "reasoning": "..." },
|
|
32
|
+
{ "index": 2, "action": "remove", "reasoning": "..." }
|
|
33
|
+
]
|
|
34
|
+
|
|
35
|
+
Return ONLY valid JSON. Do not include markdown code fences or explanatory text.
|
|
@@ -83,6 +83,17 @@ The database owner has provided the following authoritative documentation. Your
|
|
|
83
83
|
{% if seedContext.customInstructions %}- **Special Instructions**: {{ seedContext.customInstructions }}{% endif %}
|
|
84
84
|
{% endif %}
|
|
85
85
|
|
|
86
|
+
{% if fkCandidateStats and fkCandidateStats.length > 0 %}
|
|
87
|
+
## FK Evidence from Statistical Analysis
|
|
88
|
+
The following columns in this table were identified as potential foreign keys by statistical analysis. Use this evidence to inform (but not limit) your FK assessment — you may identify additional FKs not listed here.
|
|
89
|
+
|
|
90
|
+
{% for fk in fkCandidateStats %}
|
|
91
|
+
- **{{ fk.sourceColumn }}** → {{ fk.targetSchema }}.{{ fk.targetTable }}.{{ fk.targetColumn }} (value overlap: {{ (fk.valueOverlap * 100) | round(1) }}%, cardinality ratio: {{ fk.cardinalityRatio | round(2) }}, confidence: {{ fk.confidence }}%)
|
|
92
|
+
{% endfor %}
|
|
93
|
+
|
|
94
|
+
*Value overlap = % of source values that exist in the target column. 100% = strong FK evidence. Cardinality ratio = source distinct / target distinct — values > 1 suggest child→parent direction.*
|
|
95
|
+
{% endif %}
|
|
96
|
+
|
|
86
97
|
{% if allTables %}
|
|
87
98
|
## All Database Tables
|
|
88
99
|
**IMPORTANT**: When referring to foreign key relationships, you MUST use one of these exact table names:
|
|
@@ -111,6 +122,11 @@ Based on the evidence above, generate a JSON response with this exact structure:
|
|
|
111
122
|
"reasoning": "Brief explanation of the evidence"
|
|
112
123
|
}
|
|
113
124
|
],
|
|
125
|
+
"primaryKey": {
|
|
126
|
+
"columns": ["CustomerID"],
|
|
127
|
+
"confidence": 0.95,
|
|
128
|
+
"reasoning": "Single auto-increment column with 100% uniqueness, named after the table"
|
|
129
|
+
},
|
|
114
130
|
"foreignKeys": [
|
|
115
131
|
{
|
|
116
132
|
"columnName": "prd_id",
|
|
@@ -136,14 +152,23 @@ Based on the evidence above, generate a JSON response with this exact structure:
|
|
|
136
152
|
2. **Reasoning**: Reference specific evidence (column names, FK relationships, sample values, cardinality patterns)
|
|
137
153
|
3. **Confidence**: 0-1 scale. Be conservative. Use < 0.7 if ambiguous.
|
|
138
154
|
4. **Column Descriptions**: Every column should be described. Explain its role and meaning.
|
|
139
|
-
5. **
|
|
155
|
+
5. **Primary Key**: Identify the column(s) that most likely form this table's primary key.
|
|
156
|
+
- Look for columns with 100% uniqueness, zero nulls, and names like `ID`, `TableNameID`, or `Code`
|
|
157
|
+
- For junction/bridge tables (e.g., `ProductModelIllustration`), the PK is likely a composite of the FK columns
|
|
158
|
+
- If the table inherits an ID from a parent (e.g., `Employee` using `BusinessEntityID` from `BusinessEntity`), that inherited column IS the PK
|
|
159
|
+
- Use `"columns": ["Col1", "Col2"]` for composite keys
|
|
160
|
+
- Confidence should reflect how certain you are (0-1 scale)
|
|
161
|
+
- If a PK is already marked in the column list above, you may confirm it or propose a different one
|
|
162
|
+
6. **Foreign Keys**: **CRITICAL** - Use structured format for ALL foreign key relationships:
|
|
140
163
|
- Include EVERY column that references another table
|
|
141
164
|
- Use EXACT schema and table names from the "All Database Tables" list above
|
|
142
165
|
- Specify confidence (0-1 scale) based on evidence strength
|
|
143
166
|
- Example: If `prd_id` exists, add: `{"columnName": "prd_id", "referencesSchema": "inv", "referencesTable": "prd", "referencesColumn": "prd_id", "confidence": 0.95}`
|
|
144
167
|
- **Leave empty array if no foreign keys detected**
|
|
145
|
-
|
|
146
|
-
|
|
168
|
+
- **Inheritance/specialization**: When a column could reference either a generic base table (e.g., BusinessEntity) or a more specialized table (e.g., Person, Employee, Vendor) that inherits from it, **always prefer the most specialized table**. The specialized table is the one that adds domain-specific columns beyond the base table. For example, `Employee.BusinessEntityID` should reference `Person.BusinessEntityID` (not `BusinessEntity.BusinessEntityID`) because Person is the specialized entity that Employee relates to.
|
|
169
|
+
- **Polymorphic FKs**: Some columns may reference different tables depending on the row (e.g., a `ReferenceOrderID` that could point to a SalesOrder, PurchaseOrder, or WorkOrder). If you detect this pattern, pick the **single most common/likely target** and note the polymorphic nature in your reasoning. Do not create multiple FK entries for the same column pointing to different tables unless you are highly confident each is valid.
|
|
170
|
+
7. **Business Domain**: Infer from table name and purpose (e.g., "Sales", "HR", "Inventory", "Billing", "Security")
|
|
171
|
+
8. **Parent Table Insights**: If analyzing this child table reveals new information about parent tables, include it. Examples:
|
|
147
172
|
- Discovering enum values in the parent (e.g., "Member table has a 'Type' column with values: Individual, Corporate, Student")
|
|
148
173
|
- Revealing parent table classification/purpose (e.g., "BoardMember reveals that Member table includes leadership roles, not just general members")
|
|
149
174
|
- Identifying parent table patterns (e.g., "Multiple child tables suggest Organization serves as a multi-tenant partition key")
|