SQL Conversion

แปลง 4GL → SQL Query

แปลง Logic ของโปรแกรมเป็น SQL เพื่อดึงข้อมูลไปพัฒนา API

1
แปลง 4GL → SQL

แปลง SELECT ของ 4GL เป็น Standard SQL ที่ใช้ได้ทุก Database

2
สร้าง View

รวม Query ทั้งหมดเป็น View เพื่อใช้เป็น Data Source

3
ต่อ API

ดึงจาก View ด้วย REST API (Python, Node.js, etc.)

Query 1

ดึงข้อมูล COA Header + Business Partner

รวมข้อมูลหัว COA, ลูกค้า, ที่อยู่ ใน Query เดียว

4GL เดิม
select  sccoa003.*
from    sccoa003
where   sccoa003.coan >= {:coan.f}
    and sccoa003.coan <= {:coan.t}
    and sccoa003.ofbp >= {:bpid.f}
    and sccoa003.ofbp <= {:bpid.t}
selectdo

  | ดึง BP แยก
  select  tccom100.*
  from    tccom100
  where   tccom100.bpid = {:sccoa003.stbp}
  selectdo
  endselect

  | ดึง Address แยก
  select  tccom130.*
  from    tccom130
  where   tccom130.cadr = {:tccom100.cadr}
  selectdo
  endselect

endselect
Standard SQL
SELECT
    h.t$coan         AS coa_number,
    h.t$ofbp         AS ordered_by_bp,
    h.t$stbp         AS ship_to_bp,
    h.t$item         AS item_code,
    h.t$cwar         AS warehouse,
    h.t$loca         AS location,
    h.t$qnty         AS quantity,
    h.t$cuni         AS unit,
    h.t$mfgd         AS mfg_date_utc,
    h.t$expd         AS exp_date_utc,
    h.t$tbvr         AS out_of_spec_flag,
    bp.t$nama        AS bp_name,
    addr.t$ln01      AS address_line1,
    addr.t$ln02      AS address_line2,
    addr.t$ccty      AS country_code,
    CASE addr.t$ccty
        WHEN 'THA' THEN 'Local'
        ELSE 'Export'
    END              AS coa_type
FROM      sccoa003 h
LEFT JOIN tccom100 bp   ON bp.t$bpid = h.t$stbp
LEFT JOIN tccom130 addr ON addr.t$cadr = bp.t$cadr
WHERE     h.t$coan BETWEEN :coan_from AND :coan_to
  AND     h.t$ofbp BETWEEN :bpid_from AND :bpid_to
ORDER BY h.t$coan;
หมายเหตุ: ใน LN Database จริง ชื่อคอลัมน์จะเริ่มด้วย t$ เช่น t$coan, t$bpid — ใช้ LEFT JOIN แทน nested SELECT เพื่อให้ได้ข้อมูลแม้ว่าจะไม่มี BP หรือ Address
Query 2

ดึงรายละเอียดผลทดสอบ (COA Detail)

รวม Detail + Characteristic + Specification ใน Query เดียว

4GL เดิม
select sccoa004.*, qmptc001.*
from   sccoa004 
JOIN   qmptc001 
  ON   qmptc001.char = sccoa004.char
where  sccoa004.coan = {:sccoa003.coan}
selectdo
    | จัดรูปแบบตาม ctyp
    if qmptc001.ctyp = option then
        | ดึง option description
        select qmptc014.dsca ...
    else
        | format ตัวเลข
        rpt.result = edit$(sccoa004.mval, ...)
    endif
    
    | คำนวณ Spec
    if prto = min then
        rpt.spec = "Min. " & edit$(llmt, ...)
    endif
endselect
Standard SQL
SELECT
    d.t$coan         AS coa_number,
    d.t$prts         AS sort_key,
    d.t$char         AS char_code,
    c.t$dsca         AS char_description,
    c.t$ctyp         AS char_type,
    d.t$mval         AS measured_value,
    d.t$mopt         AS measured_option,
    opt.t$dsca       AS option_description,
    c.t$cuni         AS unit,
    d.t$prto         AS spec_print_option,
    d.t$llmt         AS lower_limit,
    d.t$ulmt         AS upper_limit,
    d.t$fxsp         AS fixed_spec_text,
    CASE d.t$prto
        WHEN 1 THEN 'Min. ' || CAST(d.t$llmt AS VARCHAR)
        WHEN 2 THEN 'Max. ' || CAST(d.t$ulmt AS VARCHAR)
        WHEN 3 THEN CAST(d.t$llmt AS VARCHAR) 
                   || ' - ' || CAST(d.t$ulmt AS VARCHAR)
        WHEN 6 THEN d.t$fxsp
        ELSE ''
    END              AS specification,
    CASE c.t$ctyp
        WHEN 1 THEN opt.t$dsca
        ELSE CAST(d.t$mval AS VARCHAR)
    END              AS result_display
FROM      sccoa004 d
JOIN      qmptc001 c   ON c.t$char = d.t$char
LEFT JOIN qmptc014 opt ON opt.t$oset = d.t$oset 
                        AND opt.t$optn = d.t$mopt
WHERE     d.t$coan = :coa_number
ORDER BY d.t$prts;
จุดสำคัญ: ใช้ CASE แทน if...else ใน SQL — จะได้ Specification และ Result ที่ Format แล้วในระดับ SQL เลย
Query 3

รวมทุกอย่างเป็น VIEW

สร้าง Database View เพื่อใช้เป็น Data Source สำหรับ API

CREATE VIEW — รวมทุกอย่าง
CREATE VIEW vw_coa_report AS
SELECT
    -- Header
    h.t$coan                    AS coa_number,
    h.t$item                    AS item_code,
    item.t$dsca                 AS item_description,
    h.t$stbp                    AS ship_to_bp,
    bp.t$nama                   AS bp_name,
    CONCAT(addr.t$ln01, ' ', addr.t$ln02) AS bp_address,
    addr.t$ccty                 AS country,
    CASE addr.t$ccty
        WHEN 'THA' THEN 'Local' ELSE 'Export'
    END                         AS coa_type,
    h.t$cwar                    AS warehouse,
    h.t$loca                    AS location,
    h.t$qnty                    AS quantity,
    u.t$dsca                    AS unit_desc,
    h.t$mfgd                    AS mfg_date,
    h.t$expd                    AS exp_date,
    
    -- Detail
    d.t$prts                    AS sort_key,
    d.t$char                    AS char_code,
    c.t$dsca                    AS char_name,
    c.t$ctyp                    AS char_type,
    d.t$mval                    AS measured_value,
    COALESCE(opt.t$dsca, CAST(d.t$mval AS VARCHAR))
                                AS result_display,
    d.t$llmt                    AS lower_limit,
    d.t$ulmt                    AS upper_limit,
    d.t$fxsp                    AS fixed_spec,
    d.t$prto                    AS spec_type,
    c.t$cuni                    AS char_unit

FROM      sccoa003 h
JOIN      sccoa004 d   ON d.t$coan = h.t$coan
JOIN      qmptc001 c   ON c.t$char = d.t$char
LEFT JOIN tccom100 bp  ON bp.t$bpid = h.t$stbp
LEFT JOIN tccom130 addr ON addr.t$cadr = bp.t$cadr
LEFT JOIN tcibd001 item ON item.t$item = h.t$item
LEFT JOIN tcmcs001 u   ON u.t$cuni = h.t$cuni
LEFT JOIN qmptc014 opt ON opt.t$oset = d.t$oset 
                        AND opt.t$optn = d.t$mopt
WHERE     h.t$tbvr = 0  -- เฉพาะ COA ที่ผ่าน QC
ORDER BY h.t$coan, d.t$prts;
ERD

แผนภาพความสัมพันธ์ตาราง (Entity Relationship Diagram)

แสดงตารางทั้งหมดที่ใช้ในโปรแกรม COA.4gl พร้อมเส้นเชื่อมโยง Foreign Key

Primary Key (PK)
Foreign Key (FK)
JOIN (ใช้ในโปรแกรม)
LEFT JOIN (อาจไม่มีข้อมูล)
ตารางหลัก
ตารางอ้างอิง
sccoa003 COA Header (ข้อมูลหัว)
MAIN
PK coan COA Number — เลข COA หลัก
FK stbp Ship-to BP → tccom100
FK item Item Code → tcibd001
ofbp Ordered-by BP — ลูกค้าสั่ง
cwar, loca คลัง / ตำแหน่ง
qnty, cuni จำนวน / หน่วย
mfgd, expd วันผลิต / วันหมดอายุ (UTC)
tbvr Out-of-Spec Flag (0=ผ่าน, 1=ไม่ผ่าน)
1 : N
sccoa004 COA Detail (ผลทดสอบ)
MAIN
PK coan + prts Composite Key (COA + ลำดับ)
FK coan ← sccoa003.coan
FK char Characteristic → qmptc001
mval ค่าวัดจริง (ตัวเลข)
mopt Option ที่เลือก (ถ้าเป็น Option Type)
llmt, ulmt ค่า Spec ต่ำสุด / สูงสุด
prto ประเภท Spec (1=Min, 2=Max, 3=Range, 6=Fix)
fxsp ข้อความ Spec แบบ Fixed เช่น "Clear"
stbp = bpid LEFT JOIN
item = item LEFT JOIN
char = char JOIN
tccom100 Business Partner (ข้อมูลลูกค้า)
REF
PK bpid รหัส Business Partner
nama ชื่อลูกค้า/คู่ค้า
FK cadr Address Code → tccom130
tcibd001 Item Master (ข้อมูลสินค้า)
REF
PK item รหัสสินค้า เช่น "03020"
dsca ชื่อสินค้า เช่น "Soybean Oil RBD"
qmptc001 Characteristic (คุณลักษณะ QC)
REF
PK char รหัส เช่น "COLOR", "FFA"
dsca ชื่อ เช่น "Color (Lovibond)"
ctyp ประเภท: 1=Option, 2=Fraction, 3=Integer
cuni หน่วย เช่น "Lovibond", "%"
cadr = cadr LEFT JOIN
tccom130 Address (ที่อยู่)
REF
PK cadr รหัสที่อยู่
ln01, ln02 ที่อยู่บรรทัด 1-2
ccty ประเทศ — ใช้แยก Local/Export
qmptc014 Option Set (ตัวเลือก QC)
REF
PK oset + optn Option Set + Option Code
dsca คำอธิบาย เช่น "Clear & Bright"
sccoa009 Remark Config (หมายเหตุ)
REF
PK item + bpid สินค้า + ลูกค้า (Composite)
📝 text object Remark (ดึงด้วย dal.get.text)
อธิบายความสัมพันธ์ (Relationships)
หลักการอ่านความสัมพันธ์ (Relationship) และการเชื่อม (JOIN)
  • 1 : N (หนึ่งต่อกลุ่ม): ฝั่งซ้าย 1 รายการ มีข้อมูลฝั่งขวาได้หลายรายการ (เช่น COA 1 ใบ มีผลทดสอบ หลายรายการ)
  • N : 1 (กลุ่มต่อหนึ่ง): ฝั่งซ้ายหลายรายการ เป็นของข้อมูลฝั่งขวาเดียวกัน (เช่น COA หลายใบ เป็นของ ลูกค้าคนเดียวกัน)
  • JOIN: ข้อมูลต้องมีตรงกันทั้ง 2 ฝั่งเท่านั้น ถึงจะถูกนำมาแสดง (ป้องกันข้อมูลไม่สมบูรณ์ เช่น COA ต้องมีผลทดสอบเสมอ)
  • LEFT JOIN: ยึดฝั่งซ้ายเป็นหลัก ข้อมูลจะถูกดึงมาเสมอแม้ฝั่งขวาจะแหว่งไป (เช่น ดึง COA ออกมาเสมอ แม้ลูกค้าจะถูกลบออกจากระบบไปแล้วก็ตาม)
1
sccoa003 → sccoa004 JOIN  |  1 : N

COA 1 ใบ มีผลทดสอบ หลายรายการ (เช่น Color, FFA, Moisture, ...) เชื่อมด้วย coan

JOIN sccoa004 d ON d.t$coan = h.t$coan
2
sccoa003 → tccom100 LEFT JOIN  |  N : 1

ดึงชื่อลูกค้าจาก BP Master — ใช้ stbp = bpid — LEFT JOIN เพราะ BP อาจถูกลบ

LEFT JOIN tccom100 bp ON bp.t$bpid = h.t$stbp
3
tccom100 → tccom130 LEFT JOIN  |  N : 1

ดึงที่อยู่ของ BP — ใช้ cadr — สำคัญเพราะ ccty ใช้แยก Local/Export

LEFT JOIN tccom130 addr ON addr.t$cadr = bp.t$cadr
4
sccoa004 → qmptc001 JOIN  |  N : 1

ดึงชื่อ + ประเภทของ Characteristic — ctyp สำคัญมาก ใช้ตัดสินวิธี Format ผลทดสอบ

JOIN qmptc001 c ON c.t$char = d.t$char
5
sccoa004 → qmptc014 LEFT JOIN  |  N : 1

ดึงคำอธิบาย Option — ใช้ เมื่อ ctyp = 1 (Option) เท่านั้น เช่น "Clear & Bright"

LEFT JOIN qmptc014 opt ON opt.t$oset = d.t$oset AND opt.t$optn = d.t$mopt
6
sccoa003 → tcibd001 LEFT JOIN  |  N : 1

ดึงชื่อสินค้า — ใช้ item — แสดงในหัว COA เช่น "Soybean Oil RBD"

LEFT JOIN tcibd001 item ON item.t$item = h.t$item
ลำดับการดึงข้อมูลในโปรแกรม
1
SELECT sccoa003 ดึง COA Header ตาม Range ที่กำหนด WHERE coan BETWEEN :f AND :t
2
SELECT tccom100, tccom130 ดึงชื่อ BP + ที่อยู่ + ประเทศ ใช้แยก Local / Export
3
SELECT sccoa004 + qmptc001 ดึงผลทดสอบทุกรายการของ COA นี้ Format ตาม ctyp + prto
4
SELECT sccoa009 ดึง Remark (Fallback 3 ระดับ) Item+BP → Item → Header
Data Dictionary

คำอธิบายตาราง (Table Dictionary)

รายการตารางทั้งหมดที่เกี่ยวข้องในโปรแกรม COA พร้อมคำอธิบาย

# ตาราง (Table) คำอธิบาย (Description) โมดูล (Module)
1 sccoa003 COA Header Quality
2 sccoa004 COA Details Quality
3 qmptc001 Characteristic Quality
4 qmptc014 Options Quality
5 qmptc015 QID-Characteristic Quality
6 qmptc100 Inspection Order Quality
7 tccom100 Business Partner Common
8 tccom130 Address Common
9 tccom001 Employee Common
10 tcibd001 Item General Item
11 tcibd004 Item Code System Item
12 tcmcs001 Unit Common
13 tcmcs007 Unit by Language Common
14 sccoa007 Source of Origin Quality
15 sccoa008 Spec by BP/Item Quality
16 sccoa009 Remark by Item/BP Quality
17 tdsls400 Sales Order Sales
API

ตัวอย่าง API Response (JSON)

ข้อมูลที่ได้จาก View สามารถส่งผ่าน REST API ในรูปแบบ JSON

Python Flask API
from flask import Flask, jsonify
import pyodbc

app = Flask(__name__)

@app.route('/api/coa/<coa_number>')
def get_coa(coa_number):
    conn = pyodbc.connect(LN_CONNECTION)
    cursor = conn.cursor()
    
    cursor.execute("""
        SELECT * FROM vw_coa_report
        WHERE coa_number = ?
    """, coa_number)
    
    columns = [col[0] for col in cursor.description]
    rows = [dict(zip(columns, row)) 
            for row in cursor.fetchall()]
    
    return jsonify({
        "coa_number": coa_number,
        "details": rows
    })
JSON Response
{
  "coa_number": "COA-2026-001",
  "item_code": "03020",
  "item_description": "Soybean Oil RBD",
  "bp_name": "Customer ABC Co.,Ltd.",
  "coa_type": "Export",
  "mfg_date": "2026-01-15",
  "details": [
    {
      "char_name": "Color",
      "result": "3.0",
      "spec": "Max. 4.0",
      "unit": "Lovibond"
    },
    {
      "char_name": "FFA",
      "result": "0.04",
      "spec": "Max. 0.10",
      "unit": "% as Oleic"
    },
    {
      "char_name": "Appearance",
      "result": "Clear & Bright",
      "spec": "Clear & Bright",
      "unit": ""
    }
  ]
}
สำคัญมาก

Business Logic ที่ต้อง Migrate ไปด้วย!

SQL ดึงได้แค่ข้อมูลดิบ (Raw Data) — Logic เหล่านี้ต้องย้ายไปอยู่ใน Application Layer (API Code)

SQL / View

ดึงข้อมูลดิบ + JOIN + Filter

  • JOIN ตาราง
  • WHERE filter
  • CASE WHEN (simple)
Application Logic

แปลง + จัดรูปแบบ + ตรรกะ

  • Format ตัวเลข
  • แปลงวันที่
  • คำนวณ Spec
  • Remark fallback
API Response

จัดโครงสร้าง JSON + Validate

  • Grouping (Header/Detail)
  • Error handling
  • Pagination

8 เงื่อนไขสำคัญจากโปรแกรมเดิม

1
จัดรูปแบบผลทดสอบตาม Characteristic Type
ต้องย้ายไป API

โปรแกรมเดิมใช้ qmptc001.ctyp เพื่อตัดสินว่าผลทดสอบจะแสดงอย่างไร:

4GL เดิม (บรรทัด 290–360)
if ctyp = option then
    | แสดงชื่อ Option เช่น "Clear & Bright"
    rpt.result = qmptc014.dsca
else if ctyp = fraction then
    | ทศนิยม เช่น "0.04"
    rpt.result = edit$(mval, "ZZZZZ9VD99")
else if ctyp = integer then
    | จำนวนเต็ม เช่น "185"  
    rpt.result = edit$(mval, "ZZZZZZZZZ9")
endif
Python API
def format_result(row):
    if row['char_type'] == 1:    # Option
        return row['option_description']
    elif row['char_type'] == 2:  # Fraction
        val = row['measured_value']
        # ⚠️ ตรวจ High-Precision chars
        if row['char_code'] in HIGH_PREC:
            return f"{val:.4f}"
        return f"{val:.2f}"
    else:                        # Integer
        return str(int(row['measured_value']))
2
ทศนิยมพิเศษ (High-Precision) สำหรับบาง Characteristic
ต้องย้ายไป API

บาง Characteristic ต้องแสดง 4 ทศนิยม แทน 2 — ถ้าไม่ทำ ค่าจะผิด!

4GL เดิม (บรรทัด 310–320)
| รายการ Characteristic ที่ต้องแสดง 4 ทศนิยม
if trim$(sccoa004.char) = "SP_GRAV2" 
   or trim$(sccoa004.char) = "RE_DEN25"
   or trim$(sccoa004.char) = "RI_25C" then
    rpt.result = edit$(mval, "ZZZ9VD9999")  | 4 ตำแหน่ง
else
    rpt.result = edit$(mval, "ZZZZZ9VD99")  | 2 ตำแหน่ง
endif
ใน API: ต้องมี Config List ของ char codes ที่ต้องแสดง 4 ทศนิยม — HIGH_PRECISION_CHARS = ["SP_GRAV2", "RE_DEN25", "RI_25C"]
3
คำนวณ Specification (Min/Max/Range/Fixed)
ทำใน SQL ได้บางส่วน

Specification มี 6 ประเภท — ทำใน SQL ได้แค่ Simple CASE, แต่ Format ตัวเลขต้องทำใน API:

prtoประเภทตัวอย่างผลลัพธ์ทำใน SQL?ทำใน API?
1MinMin. 0.05✅ CASE⚠️ Format decimal
2MaxMax. 4.0✅ CASE⚠️ Format decimal
3Range0.05 - 0.10✅ CASE⚠️ Format both
4Min ValueMin. 185✅ CASE⚠️ Format integer
5Max ValueMax. 195✅ CASE⚠️ Format integer
6FixedClear & Bright✅ ดึง fxspไม่ต้อง
แนะนำ: ดึง llmt, ulmt, prto, fxsp เป็น Raw Data จาก SQL แล้วไป Format ใน API — เพราะ Format ตัวเลขใน SQL มีข้อจำกัด (ลบ trailing zero ยาก)
4
แปลงวันที่ UTC → DD/MM/YYYY
ต้องย้ายไป API

LN เก็บวันที่เป็น UTC integer ไม่ใช่ DATE type — ต้องแปลงเอง:

4GL เดิม (บรรทัด 675–703)
function convert.utc.to.date()
    date.l = 0
    utc.to.date(utc.i, date.l)
    date.to.num(date.l, num.i)
endfunction
| ใช้: rpt.mfgd = edit$(num.i)
| format: DD/MM/YYYY
Python API
from datetime import datetime, timedelta

# LN UTC = seconds since 1970-01-01
def utc_to_date(utc_val):
    if utc_val == 0:
        return None
    dt = datetime(1970, 1, 1) + timedelta(
        seconds=utc_val
    )
    return dt.strftime("%d/%m/%Y")
5
Remark Fallback Logic (ลำดับการค้นหาหมายเหตุ)
ต้องย้ายไป API

โปรแกรมดึง Remark แบบมี Fallback 3 ระดับ — SQL อย่างเดียวทำไม่ได้:

1
sccoa009 (Item + BP) ค้นหา Remark เฉพาะ Item + Business Partner
ไม่พบ?
2
sccoa009 (Item only) ค้นหา Remark ตาม Item อย่างเดียว (BP ว่าง)
ไม่พบ?
3
sccoa003.remk ใช้ Remark จาก COA Header เอง
ทำไมทำใน SQL ไม่ได้? เพราะ sccoa009 เก็บ Remark เป็น Text Object — ต้องเรียก dal.get.text() ดึงข้อความหลายบรรทัด ซึ่ง SQL ธรรมดาเข้าถึง Text Object ไม่ได้
6
Logic เฉพาะลูกค้า (Hardcoded BP)
ย้ายไป Config

มี Logic พิเศษสำหรับลูกค้าเฉพาะ (เช่น Nestlé = BP 110472) — ควรย้ายไปเป็น Config Table:

4GL เดิม (บรรทัด 570–580)
| Vendor Site Info สำหรับ Nestlé เท่านั้น
if sccoa003.stbp(1;6) = "110472" then
    Vender_No   = "VenderSiteNo: 100786789"
    Vender_Manu = "VenderSiteCity: SamutPrakan TH"
endif

| Packaging สำหรับ Item 03020 เท่านั้น
if trim$(tcibd001.item) = "03020" then
    rpt.packaging = "IBC"
endif
แนะนำ: สร้างตาราง Config หรือ JSON file เก็บ mapping เหล่านี้ — {"110472": {"vendor_no": "100786789", "vendor_city": "SamutPrakan TH"}} เพื่อจะได้แก้ Config ได้โดยไม่ต้องแก้ Code
7
ตรวจสอบ QC ผ่านหรือไม่ (Out-of-Spec Flag)
ทำใน SQL ได้

โปรแกรมเดิมไม่พิมพ์ COA ถ้า tbvr = 1 (Out of Spec) — อันนี้ทำใน SQL ได้เลย:

SQL WHERE clause
WHERE h.t$tbvr = 0    -- 0 = ผ่าน QC, 1 = ไม่ผ่าน
  AND h.t$coan BETWEEN :from AND :to
8
แยก Local / Export (ตามประเทศลูกค้า)
ทำใน SQL ได้

โปรแกรมเดิมใช้แยกรูปแบบรายงาน — ใน API ใช้แยกเงื่อนไขการแสดงผลได้:

SQL CASE
CASE addr.t$ccty 
    WHEN 'THA' THEN 'Local'    -- ในประเทศ
    ELSE 'Export'               -- ส่งออก
END AS coa_type
ใน API: ใช้ coa_type เพื่อเลือก Template ที่ต่างกัน — เช่น Export อาจต้องแสดง Vendor Info เพิ่ม หรือใช้ภาษาอังกฤษเท่านั้น

สรุป: อะไรอยู่ไหน?

Logic ทำใน SQL? ทำใน API? เหตุผล
JOIN ตาราง ✅ ใช่ SQL ถนัด JOIN
Filter QC Pass/Fail ✅ ใช่ WHERE clause
Local vs Export ✅ ใช่ CASE WHEN
Format ตัวเลข (ทศนิยม/จำนวนเต็ม) ⚠️ ใช่ SQL format ตัวเลขลำบาก
High-Precision (4 ทศนิยม) ⚠️ ใช่ ต้องมี Config list
คำนวณ Specification ✅ บางส่วน ⚠️ Format CASE logic + API format
แปลงวันที่ UTC ⚠️ ใช่ UTC integer → Date
Remark Fallback 🔴 ต้องทำ ต้องเรียก Text Object
Hardcoded BP/Item 🔴 Config ย้ายไป Config file

ตัวอย่าง API ที่รวม Logic ครบ

Python Flask — Full API with Business Logic
import pyodbc
from flask import Flask, jsonify
from datetime import datetime, timedelta

# ===== Config (ย้ายมาจาก Hardcode ในโปรแกรม) =====
HIGH_PRECISION_CHARS = ["SP_GRAV2", "RE_DEN25", "RI_25C"]

BP_CONFIG = {
    "110472": {
        "vendor_no": "VenderSiteNo: 100786789",
        "vendor_city": "VenderSiteCity: SamutPrakan TH"
    }
}

ITEM_PACKAGING = {"03020": "IBC"}

# ===== Business Logic Functions =====
def utc_to_date(utc_val):
    """Logic จากบรรทัด 675-703 ของ COA.4gl"""
    if not utc_val or utc_val == 0:
        return None
    dt = datetime(1970, 1, 1) + timedelta(seconds=utc_val)
    return dt.strftime("%d/%m/%Y")

def format_result(char_code, char_type, mval, opt_desc):
    """Logic จากบรรทัด 290-360 ของ COA.4gl"""
    if char_type == 1:  # Option
        return opt_desc or ""
    elif char_type == 2:  # Fraction
        decimals = 4 if char_code.strip() in HIGH_PRECISION_CHARS else 2
        return f"{mval:.{decimals}f}"
    else:  # Integer
        return str(int(mval)) if mval else "0"

def format_spec(prto, llmt, ulmt, fxsp, char_code, char_type):
    """Logic จากบรรทัด 409-572 ของ COA.4gl"""
    fmt = lambda v: format_result(char_code, char_type, v, None)
    specs = {
        1: lambda: f"Min. {fmt(llmt)}",
        2: lambda: f"Max. {fmt(ulmt)}",
        3: lambda: f"{fmt(llmt)} - {fmt(ulmt)}",
        4: lambda: f"Min. {fmt(llmt)}",
        5: lambda: f"Max. {fmt(ulmt)}",
        6: lambda: fxsp or "",
    }
    return specs.get(prto, lambda: "")()

@app.route('/api/coa/<coa_number>')
def get_coa(coa_number):
    cursor.execute("SELECT * FROM vw_coa_report WHERE coa_number = ?", coa_number)
    rows = cursor.fetchall()
    
    if not rows:
        return jsonify({"error": "COA not found"}), 404
    
    header = rows[0]
    bp_extra = BP_CONFIG.get(header.ship_to_bp.strip()[:6], {})
    
    return jsonify({
        "coa_number": coa_number,
        "coa_type": header.coa_type,
        "mfg_date": utc_to_date(header.mfg_date),  # ← Logic #4
        "exp_date": utc_to_date(header.exp_date),
        "vendor_info": bp_extra,                   # ← Logic #6
        "packaging": ITEM_PACKAGING.get(header.item_code.strip(), ""),
        "details": [{
            "name": r.char_name,
            "result": format_result(                 # ← Logic #1, #2
                r.char_code, r.char_type, r.measured_value, r.option_desc),
            "spec": format_spec(                      # ← Logic #3
                r.spec_type, r.lower_limit, r.upper_limit, 
                r.fixed_spec, r.char_code, r.char_type),
            "unit": r.char_unit
        } for r in rows]
    })
Try it Out!

Interactive API API Mockup

จำลองการเรียกใช้งาน REST API ดูผลลัพธ์ข้อมูลจริงๆ จากโครงสร้างที่ได้ออกแบบไว้ข้างต้น (จำลอง)

ข้อมูลการค้นหา (Request Parameters)
GET
รหัสใบรับรองผลวิเคราะห์ (ไม่ต้องระบุช่วง From-To หากระบุใบเดียว)
Response JSON
// กด "Send Request" เพื่อดูผลลัพธ์
// (สามารถเปลี่ยน COA Number เป็นอย่างอื่นเพื่อดูการตอบสนองที่เปลี่ยนไป)

เปรียบเทียบ 4GL vs SQL

หัวข้อ 4GL ในโปรแกรม Standard SQL
การ Join ตาราง select ซ้อน select (N+1 query) JOIN ... ON (1 query)
Conditional Logic if...else...endif ซ้อนกัน CASE WHEN...THEN...END
Format ตัวเลข edit$(..., "ZZZ9VD99") FORMAT() หรือ CAST()
String Concat trim$() & strip$() CONCAT() หรือ ||
Host Variable {:variable} :param หรือ ?
No Data Found selectempty LEFT JOIN + COALESCE()
End of Query selecteos ไม่จำเป็น (Process หลัง query)