# Business Logic — JBSbatKKDelSavePrdKikData.countChk() [109 LOC]

| Field | Value |
|-------|-------|
| Fully Qualified Name | `eo.business.service.JBSbatKKDelSavePrdKikData` |
| Layer | Service (Batch processing service) |
| Module | `service` (Package: `eo.business.service`) |

## 1. Role

### JBSbatKKDelSavePrdKikData.countChk()

This method implements a referential integrity guard for the batch-driven physical data deletion process in the K-Opticom customer base system (eo customer base system / eo顧客基盤システム). Specifically, it verifies that the set of child schema records targeted for deletion is exactly equivalent to the set of child records currently linked to the parent schema in the database — ensuring no "straggler" (deletion-target-excluded / 削除対象外) records exist on the parent before allowing the deletion to proceed.

The method operates by executing two independent aggregate queries against the same database table: the first counts only those parent records whose child links match the deletion candidate keys, while the second counts all parent records linked to the target child (regardless of whether they are in the deletion set). If both counts match, it means every linked child record on the parent is in the deletion set — deletion is safe. If they differ, there are child records linked to the parent that are NOT in the deletion set — deletion must be blocked to preserve referential integrity.

This is a **shared utility** method, callable from any deletion-preparation routine within the batch job. It implements a **guard gate / pre-check** design pattern, acting as a validation layer between the key-retrieval phase and the actual deletion execution phase. It does not perform any data mutation — it is strictly a read-only integrity check.

The method is used for schema-level referential integrity checks during contract cancellation batch processing (契約削除バッチ), specifically protecting against partial deletion scenarios where deleting only a subset of child records would leave orphaned or inconsistent parent-child relationships.

## 2. Processing Pattern (Detailed Business Logic)

```mermaid
flowchart TD
    START(["countChk(TBL, searchKey, strkey, cntKey, delKeyInfo)"])

    START --> INIT["Initialize: result=false, chkCount1=0, chkCount2=0, loopEnd=delKeyInfo.size()"]

    INIT --> COND1{loopEnd != 0?}

    COND1 -- false --> SKIP_SQL1[Skip SQL1 execution]

    COND1 -- true --> BUILD_SQL1["Build SQL1:
SELECT COUNT(DISTINCT searchKey) FROM TBL
WHERE strkey = cntKey AND searchKey IS NOT NULL
AND searchKey IN (delKeyInfo values) AND MK_FLG = '0'"]

    BUILD_SQL1 --> LOG_SQL1["Debug log: SQL1 statement"]

    LOG_SQL1 --> CREATE_ACCESS1["Create db_CNT_TABLE with TBL"]

    CREATE_ACCESS1 --> PREPARE1["Create PreparedStatement pstatmt1"]

    PREPARE1 --> EXEC1["ExecuteQuery: SELECT count(*)"]

    EXEC1 --> READ1["Read ResultSet:
while rs1.next() { chkCount1 = rs1.getInt(1) }"]

    READ1 --> CLOSE1["Close rs1, pstatmt1, db_CNT_TABLE"]

    CLOSE1 --> SKIP_SQL1

    SKIP_SQL1 --> BUILD_SQL2["Build SQL2:
SELECT COUNT(DISTINCT searchKey) FROM TBL
WHERE strkey = cntKey AND searchKey IS NOT NULL
AND MK_FLG = '0'"]

    BUILD_SQL2 --> LOG_SQL2["Debug log: SQL2 statement"]

    LOG_SQL2 --> CREATE_ACCESS2["Create db_CNT_TABLE with TBL"]

    CREATE_ACCESS2 --> PREPARE2["Create PreparedStatement pstatmt2"]

    PREPARE2 --> EXEC2["ExecuteQuery: SELECT count(*)"]

    EXEC2 --> READ2["Read ResultSet:
while rs2.next() { chkCount2 = rs2.getInt(1) }"]

    READ2 --> CLOSE2["Close rs2, pstatmt2, db_CNT_TABLE"]

    CLOSE2 --> COMPARE{chkCount1 == chkCount2?}

    COMPARE -- true --> SET_TRUE["result = true"]

    COMPARE -- false --> SET_FALSE["result = false (default)"]

    SET_TRUE --> END_RETURN(["return result"])
    SET_FALSE --> END_RETURN
```

**Processing summary:**

1. **Initialization (L7295–7298):** All counting variables are set to zero and `result` defaults to `false` (conservative: deletion is blocked unless proven safe).
2. **Conditional SQL-1 execution (L7300–L7338):** If `delKeyInfo` is non-empty, SQL-1 is built to count distinct parent schema records whose child links are among the deletion candidates. This query is only necessary when there are actual deletion targets.
3. **SQL-2 execution (L7342–L7373):** SQL-2 always runs (regardless of whether `delKeyInfo` is empty). It counts ALL distinct parent records linked to the target child, without filtering by deletion candidates. This establishes the baseline "total" count.
4. **Comparison (L7378–7384):** The two counts are compared. If they are equal, every linked child is in the deletion set — deletion is permitted (`true`). If they differ, there are un-deletable child records — deletion is blocked (`false`).

**CRITICAL — Constant Resolution:**

| Constant | Value | Business Meaning |
|----------|-------|-----------------|
| `MK_FLG` | `'0'` | Active/valid record flag — records with `MK_FLG = '0'` are active and subject to processing; non-zero values indicate inactive/excluded records |

## 3. Parameter Analysis

| No | Parameter Name | Type | Business Description |
|----|---------------|------|---------------------|
| 1 | `TBL` | `String` | Parent schema (table) name — the database table containing both the parent records and their linked child records. Examples include `KK_T_SVC_KEI` (Service Contract), `KK_T_SVC_KEI_UCWK` (Service Contract Line Detail), `KK_T_DCHSKMST` (Data Extraction Setting), etc. |
| 2 | `searchKey` | `String` | The column/field name whose distinct count is being computed — typically a parent identifier such as `svc_kei_no` (Service Contract Number) or `svc_kei_ucwk_no` (Service Contract Line Detail Number). |
| 3 | `strkey` | `String` | The join/link column that connects parent and child schemas — typically the foreign key or association field. For example, a child record's foreign key pointing back to the parent contract. |
| 4 | `cntKey` | `String` | The value used to match the parent schema against the target child — identifies which parent-child linkage is being evaluated. Represents the specific child key for which the count relationship is being verified. |
| 5 | `delKeyInfo` | `ArrayList<String>` | List of deletion candidate keys (parent schema values targeted for deletion). This is the set of parent records the caller intends to delete. If empty (size 0), the first SQL is skipped and only the baseline count is computed. |

**Instance fields read by this method:**

| Field | Type | Business Description |
|-------|------|---------------------|
| `db_CNT_TABLE` | `JBSbatSQLAccess` | Database access handler for executing the COUNT queries against the parent schema. Instantiated and closed within this method per query pair. |
| `commonItem` | `JBSbatCommonItem` | Common business item passed to the `JBSbatSQLAccess` constructor — contains the database connection context. |
| `logPrint` (inherited via `super`) | debug log handler | Used to output the constructed SQL statements as debug logs for audit/tracing purposes. |

## 4. CRUD Operations / Called Services

### Pre-computed evidence from code analysis graph:

| CRUD | SC / CBS | SC Code | Entity / DB | Operation Description |
|------|----------|---------|-------------|----------------------|
| R | `printDebugLog` | JACBatCommon | - | Logs the first SQL statement (SQL1) for audit/debug. Prints "【削除対象スキーマに紐付き、かつ親スキーマで削除対象となる件数】" (Count of records linked to the deletion-target schema AND that become deletion targets in the parent schema). |
| R | `JBSbatSQLAccess.createStatement` | JBSbatSQLAccess | - | Creates a PreparedStatement from the SQL1 string — sets up the parent schema access with `TBL` as the target table name. |
| R | `PreparedStatement.executeQuery` | JDBC | `TBL` (varies) | Executes the SQL1 SELECT COUNT(DISTINCT ...) query. Counts distinct parent keys linked to deletion-target children with `MK_FLG = '0'`. |
| R | `ResultSet.getInt(1)` | JDBC | - | Reads the aggregate count result into `chkCount1`. |
| R | `printDebugLog` | JACBatCommon | - | Logs the result: "TBL1：{TBL}：検索KEY1：{strkey}：検索件数1：{chkCount1}". |
| R | `printDebugLog` | JACBatCommon | - | Logs the second SQL statement (SQL2) for audit/debug. Prints "【削除対象スキーマに紐付く親スキーマの件数を検索】" (Search for the count of parent schemas linked to the deletion-target schema). |
| R | `JBSbatSQLAccess.createStatement` | JBSbatSQLAccess | - | Creates a PreparedStatement from the SQL2 string — sets up access for the baseline count query. |
| R | `PreparedStatement.executeQuery` | JDBC | `TBL` (varies) | Executes the SQL2 SELECT COUNT(DISTINCT ...) query. Counts ALL distinct parent keys linked to the target child (without filtering by deletion candidates). |
| R | `ResultSet.getInt(1)` | JDBC | - | Reads the aggregate count result into `chkCount2`. |
| R | `printDebugLog` | JACBatCommon | - | Logs the result: "TBL2：{TBL}：検索KEY2：{strkey}：検索件数2：{chkCount2}". |
| - | `JBSbatSQLAccess.close` | JBSbatSQLAccess | - | Closes the database access handler after each query pair to release DB resources. Called twice (once after SQL1, once after SQL2). |
| - | `PreparedStatement.close` | JDBC | - | Closes the prepared statement. Called twice (once after SQL1, once after SQL2). |
| - | `ResultSet.close` | JDBC | - | Closes the result set. Called twice (once after SQL1, once after SQL2). |

**CRUD Classification:**

All operations within this method are **Read (R)**. The method performs zero data modification — it is a pure integrity verification. The two COUNT queries against the dynamic table (`TBL`) are the sole database interactions.

## 5. Dependency Trace

### Pre-computed evidence from code analysis graph:

No screen/batch entry points found within 8 hops. Direct callers found: 2 methods.
Terminal operations from this method: `close` [-], `close` [-], `close` [-], `close` [-], `close` [-], `close` [-], `printDebugLog` [-], `printDebugLog` [-], `printDebugLog` [-], `printDebugLog` [-], `printDebugLog` [-], `close` [-], `close` [-], `close` [-]

| # | Caller (Screen/Batch) | Call Chain (Full Path to this Method) | Terminal (SC / CRUD / Entity) |
|---|----------------------|--------------------------------------|-------------------------------|
| 1 | Batch: `JBSbatKKDelSavePrdKikData.delPsbChk()` | `delPsbChk()` -> `countChk()` | `PreparedStatement.executeQuery [R] TBL (dynamic)` |
| 2 | Batch: `JBSbatKKDelSavePrdKikData.mskmdelPsbChk()` | `mskmdelPsbChk()` -> `countChk()` | `PreparedStatement.executeQuery [R] TBL (dynamic)` |

**Caller descriptions:**

- **`delPsbChk()`** — Deletion possibility check. Invoked during the deletion preparation phase to validate that the deletion candidate keys for a given schema have no excluded (削除対象外) linked records on the parent. Called from the main deletion flow to gate whether deletion can proceed.

- **`mskmdelPsbChk()`** — Masking deletion possibility check. A variant of the deletion possibility check used in masking-related deletion contexts (マスク処理関連の削除チェック). Uses the same integrity verification pattern.

Both callers invoke `countChk` as part of a multi-step deletion validation pipeline — the method serves as the final integrity gate before the actual batch deletion SQL is executed.

## 6. Per-Branch Detail Blocks

**Block 1** — [INITIALIZATION] (L7295–7298)

> Initialize all working variables to safe defaults before query execution. `result` defaults to `false` (deletion blocked by default — fail-secure principle).

| # | Type | Code |
|---|------|------|
| 1 | SET | `result = false` | default — deletion not allowed until proven safe |
| 2 | SET | `chkCount1 = 0` | count of parent records whose child links are in deletion set |
| 3 | SET | `chkCount2 = 0` | count of ALL parent records linked to the target child |
| 4 | SET | `loopEnd = delKeyInfo.size()` | number of deletion candidate keys |

**Block 2** — [IF] `(loopEnd != 0)` (L7300)

> Only build and execute SQL-1 if there are actual deletion targets to check. If `delKeyInfo` is empty, there are no candidates and SQL-1 is skipped; the comparison at the end still works because `chkCount1` remains 0.

| # | Type | Code |
|---|------|------|
| 1 | SET | `sqlBuff1 = new StringBuffer()` | prepare SQL buffer for first query |
| 2 | SET | `sqlBuff1.append("SELECT COUNT(DISTINCT " + searchKey + ") FROM " + TBL)` | start SELECT clause — counts distinct parent keys |
| 3 | SET | `sqlBuff1.append(" WHERE " + strkey + " = '" + cntKey + "' AND " + searchKey + " IS NOT NULL AND " + searchKey + " IN ('" + delKeyInfo.get(0) + "' ")` | WHERE clause: strkey equals cntKey, searchKey not null, and searchKey IN first delKey value |
| 4 | FOR | `for(int i = 1 ; i < loopEnd ; i++)` | loop through remaining deletion candidate keys |
| 5 | SET | `sqlBuff1.append(",'" + delKeyInfo.get(i) + "' ")` | append each remaining delKey to IN clause |
| 6 | SET | `sqlBuff1.append(") AND MK_FLG = '0'")` | finish SQL: restrict to active records [MK_FLG="0"] |
| 7 | EXEC | `super.logPrint.printDebugLog("【削除対象スキーマに紐付き、かつ親スキーマで削除対象となる件数】" + sqlBuff1.toString())` | log SQL1 [English: "Count linked to deletion-target schema AND becoming deletion target in parent"] |
| 8 | SET | `db_CNT_TABLE = new JBSbatSQLAccess(commonItem, TBL)` | instantiate DB access handler with parent schema name |
| 9 | SET | `pstatmt1 = db_CNT_TABLE.createStatement(sqlBuff1.toString())` | create PreparedStatement from SQL1 |
| 10 | EXEC | `rs1 = pstatmt1.executeQuery()` | execute SQL1 query |
| 11 | WHILE | `while (rs1.next())` | iterate result set (exactly one row for COUNT) |
| 12 | SET | `chkCount1 = rs1.getInt(1)` | read first column — the COUNT result |
| 13 | EXEC | `rs1.close()` | close result set |
| 14 | EXEC | `super.logPrint.printDebugLog("TBL1：" + TBL + "：検索KEY1：" + strkey + "：検索件数1：" + chkCount1)` | log count1 result |
| 15 | EXEC | `pstatmt1.close()` | close prepared statement |
| 16 | IF | `if (db_CNT_TABLE != null)` | null-safe close |
| 17 | EXEC | `db_CNT_TABLE.close()` | close DB access handler |

**Block 3** — [INITIALIZATION] (L7342–7343)

> Reset SQL buffer for the second query (baseline count, always executed).

| # | Type | Code |
|---|------|------|
| 1 | SET | `sqlBuff2 = new StringBuffer()` | prepare SQL buffer for second query |
| 2 | SET | `sqlBuff2.append("SELECT COUNT(DISTINCT " + searchKey + ") FROM " + TBL)` | start SELECT clause |
| 3 | SET | `sqlBuff2.append(" WHERE " + strkey + " = '" + cntKey + "' AND " + searchKey + " IS NOT NULL")` | WHERE: strkey matches cntKey, searchKey not null |
| 4 | SET | `sqlBuff2.append(" AND MK_FLG = '0'")` | restrict to active records [MK_FLG="0"] |

**Block 4** — [SQL-2 EXECUTION] (L7342–7373)

> Always executes regardless of `delKeyInfo` size. This query counts ALL parent records linked to the target child (no deletion-candidate filter), establishing the baseline total. This is the "denominator" in the integrity check.

| # | Type | Code |
|---|------|------|
| 1 | EXEC | `super.logPrint.printDebugLog("【削除対象スキーマに紐付く親スキーマの件数を検索】" + sqlBuff2.toString())` | log SQL2 [English: "Search for count of parent schemas linked to deletion-target schema"] |
| 2 | SET | `db_CNT_TABLE = new JBSbatSQLAccess(commonItem, TBL)` | instantiate fresh DB access handler |
| 3 | SET | `pstatmt2 = db_CNT_TABLE.createStatement(sqlBuff2.toString())` | create PreparedStatement from SQL2 |
| 4 | EXEC | `rs2 = pstatmt2.executeQuery()` | execute SQL2 query |
| 5 | WHILE | `while (rs2.next())` | iterate (exactly one row) |
| 6 | SET | `chkCount2 = rs2.getInt(1)` | read COUNT result |
| 7 | EXEC | `rs2.close()` | close result set |
| 8 | EXEC | `super.logPrint.printDebugLog("TBL2：" + TBL + "：検索KEY2：" + strkey + "：検索件数2：" + chkCount2)` | log count2 result |
| 9 | EXEC | `pstatmt2.close()` | close prepared statement |
| 10 | IF | `if (db_CNT_TABLE != null)` | null-safe close |
| 11 | EXEC | `db_CNT_TABLE.close()` | close DB access handler |

**Block 5** — [IF] `(chkCount1 == chkCount2)` (L7379–7382)

> ANK-1655-00-00 (2014/02/25) — Integrity comparison gate. If the count of parent records linked to deletion-target children equals the count of ALL linked parent records, it means ALL linked children are in the deletion set — no stragglers exist.

| # | Type | Code |
|---|------|------|
| 1 | IF (ELSE) | `if (chkCount1 == chkCount2)` | all linked children are deletion candidates? |
| 2 | SET | `result = true` | YES — deletion is safe |
| 3 | SET | — (implicit `result = false`) | NO — stragglers exist, deletion blocked (default value) |

**Block 6** — [RETURN] (L7384)

| # | Type | Code |
|---|------|------|
| 1 | RETURN | `return result` | true=deletable, false=not deletable |

## 7. Glossary

| Term | Type | Business Meaning |
|------|------|------------------|
| `TBL` | Parameter | Parent schema name — the database table containing both parent and child records in a parent-child relationship |
| `searchKey` | Parameter | Search key column — the column whose distinct values are counted; typically a parent identifier such as service contract number |
| `strkey` | Parameter | String key — the join/link column connecting parent and child schemas; typically a foreign key field |
| `cntKey` | Parameter | Count key value — the value used to match the parent schema; identifies which parent-child linkage is being evaluated |
| `delKeyInfo` | Parameter | Deletion key info — an ArrayList of parent schema values targeted for deletion |
| `MK_FLG` | Field | Mark flag — an active/inactive record flag; `MK_FLG = '0'` means the record is active and subject to processing |
| `chkCount1` | Variable | First check count — number of distinct parent records linked to children that ARE in the deletion set |
| `chkCount2` | Variable | Second check count — number of ALL distinct parent records linked to the target child (regardless of deletion status) |
| `db_CNT_TABLE` | Instance | Database count table access handler — `JBSbatSQLAccess` instance used for executing COUNT queries |
| Parent Schema | Domain | The "parent" table in a parent-child relationship; records that contain child references via foreign keys |
| Child Schema | Domain | The "child" table; records that reference the parent via foreign key/link columns |
| 削除対象 (sakujoutai) | Japanese | Deletion target — a record designated for deletion by the batch process |
| 削除対象外 (sakujoutai-gai) | Japanese | Deletion-excluded / non-deletion-target — a child record that is NOT in the deletion set |
| 親スキーマ (oya schema) | Japanese | Parent schema — the parent table in a parent-child data relationship |
| 紐付き (nuitsuki) | Japanese | Linked / associated — records connected via a foreign key relationship |
| 件数 (kensuu) | Japanese | Count / number of records |
| 削除可能 (sakujokanō) | Japanese | Deletable — parent records can be safely deleted because no straggler children exist |
| 削除不可 (sakujoku-fu) | Japanese | Not deletable — deletion must be blocked because non-deletion-target children still exist on the parent |
| K-Opticom | Domain | Telecom service provider; the enterprise system context |
| 契約削除 (keiyaku sakujyo) | Japanese | Contract cancellation — the business process of deleting customer service contracts |
| バッチ処理 (batch shori) | Japanese | Batch processing — the offline, scheduled processing mode for data deletion |
| JBSbatBusinessService | Framework | Base class providing common business service functionality including inherited `logPrint` (debug logging) |
| JBSbatSQLAccess | Framework | Database access class wrapping JDBC operations for SQL execution against K-Opticom tables |
| JBSbatCommonItem | Framework | Common business item containing shared context (e.g., database connection, transaction info) |
| PreparedStatement | Framework | JDBC object for parameterized SQL execution |
| ResultSet | Framework | JDBC object holding the result of a SQL query |
