-- ============================================================================ -- پیش‌بررسی دوم — سه سؤال باز -- ============================================================================ -- -- ⚠️ مثل فایل اول: فقط SELECT COUNT / SUM. صفر INSERT/UPDATE/DELETE. -- هیچ ستون متنی در خروجی نیست. -- -- اجرا: -- mysql -u USER -p -h HOST -P 3305 oapi < migration-precheck-2.sql > out2.txt -- -- مرجع: docs/DATA-MIGRATION-MAP.md §۱۰.۴ · §۱۰.۶ -- ============================================================================ -- --------------------------------------------------------------------------- -- 🔴 سؤال ۱ (مسدودکننده) — مبلغ سند مالی در کدام ستون است؟ -- -- در خروجی اول، SUM(cashes.remain) و SUM(cashes.rial_remain) هر دو NULL -- برگشتند — یعنی در هر ۶۴۸ ردیف NULL هستند. پس مبلغ جای دیگری است. -- بدون جواب این، LedgerEntry.amount خالی پر می‌شود و کل دفتر مالی -- بی‌مبلغ منتقل می‌شود. -- --------------------------------------------------------------------------- SELECT '=== 1. WHERE IS THE LEDGER AMOUNT? ===' AS section; -- هر ستون عددی cashes: چند ردیف مقدار غیرNULL و غیرصفر دارند؟ SELECT 'input' AS col, COUNT(input) AS not_null, SUM(input <> 0) AS nonzero FROM cashes WHERE deleted_at IS NULL UNION ALL SELECT 'output', COUNT(output), SUM(output <> 0) FROM cashes WHERE deleted_at IS NULL UNION ALL SELECT 'remain', COUNT(remain), SUM(remain <> 0) FROM cashes WHERE deleted_at IS NULL UNION ALL SELECT 'rial_input', COUNT(rial_input), SUM(rial_input <> 0) FROM cashes WHERE deleted_at IS NULL UNION ALL SELECT 'rial_output', COUNT(rial_output), SUM(rial_output <> 0) FROM cashes WHERE deleted_at IS NULL UNION ALL SELECT 'rial_remain', COUNT(rial_remain), SUM(rial_remain <> 0) FROM cashes WHERE deleted_at IS NULL UNION ALL SELECT 'value', COUNT(value), SUM(value <> 0) FROM cashes WHERE deleted_at IS NULL UNION ALL SELECT 'original_value', COUNT(original_value), SUM(original_value <> 0) FROM cashes WHERE deleted_at IS NULL UNION ALL SELECT 'balance', COUNT(balance), SUM(balance <> 0) FROM cashes WHERE deleted_at IS NULL UNION ALL SELECT 'price', COUNT(price), SUM(price <> 0) FROM cashes WHERE deleted_at IS NULL UNION ALL SELECT 'price_per_gram', COUNT(price_per_gram), SUM(price_per_gram <> 0) FROM cashes WHERE deleted_at IS NULL UNION ALL SELECT 'discount', COUNT(discount), SUM(discount <> 0) FROM cashes WHERE deleted_at IS NULL; -- جمع کل هر ستونی که مقدار دارد → مبنای تطبیق برای ابزار SELECT CAST(SUM(input) AS CHAR) AS sum_input, CAST(SUM(output) AS CHAR) AS sum_output, CAST(SUM(value) AS CHAR) AS sum_value, CAST(SUM(original_value) AS CHAR) AS sum_original_value, CAST(SUM(rial_input) AS CHAR) AS sum_rial_input, CAST(SUM(rial_output) AS CHAR) AS sum_rial_output, CAST(SUM(price) AS CHAR) AS sum_price FROM cashes WHERE deleted_at IS NULL; -- به تفکیک نوع سند: کدام نوع از کدام ستون استفاده می‌کند؟ -- (type: 9=سفارش 1=آب‌شده 2=حواله 3=چک 4=پولی) SELECT type AS cash_type, COUNT(*) AS n, SUM(input IS NOT NULL AND input <> 0) AS has_input, SUM(output IS NOT NULL AND output <> 0) AS has_output, SUM(value IS NOT NULL AND value <> 0) AS has_value, SUM(rial_input IS NOT NULL AND rial_input <> 0) AS has_rial_in, SUM(rial_output IS NOT NULL AND rial_output <> 0) AS has_rial_out, SUM(price IS NOT NULL AND price <> 0) AS has_price FROM cashes WHERE deleted_at IS NULL GROUP BY type ORDER BY type; -- آیا اصلاً ردیفی هست که هیچ مبلغی نداشته باشد؟ SELECT 'rows_with_no_amount_at_all' AS metric, COUNT(*) AS n FROM cashes WHERE deleted_at IS NULL AND COALESCE(input,0)=0 AND COALESCE(output,0)=0 AND COALESCE(value,0)=0 AND COALESCE(rial_input,0)=0 AND COALESCE(rial_output,0)=0 AND COALESCE(price,0)=0; -- --------------------------------------------------------------------------- -- 🔴 سؤال ۲ — موبایل تکراری: آیا قاعده‌ی «مسئول فروش برنده» کافی است؟ -- -- ۲۷ گروه تکراری وجود دارد. اگر در یک گروه هر دو ردیف مشتری عادی باشند -- (type != 1)، آن قاعده جواب نمی‌دهد و قاعده‌ی پشتیبان لازم است. -- --------------------------------------------------------------------------- SELECT '=== 2. DUPLICATE MOBILE — is the "sales expert wins" rule enough? ===' AS section; -- در هر گروه تکراری، چند ردیف type=1 (کارشناس) هست؟ -- experts_in_group = 1 → قاعده جواب می‌دهد -- experts_in_group = 0 → هیچ کارشناسی نیست، قاعده بی‌اثر → پشتیبان لازم -- experts_in_group > 1 → چند کارشناس با یک شماره، قاعده مبهم SELECT experts_in_group, COUNT(*) AS n_groups, SUM(rows_in_group) AS n_rows FROM ( SELECT mobile, COUNT(*) AS rows_in_group, SUM(type = 1) AS experts_in_group FROM customers WHERE deleted_at IS NULL AND mobile IS NOT NULL AND mobile <> '' GROUP BY mobile HAVING COUNT(*) > 1 ) g GROUP BY experts_in_group ORDER BY experts_in_group; -- آیا ردیف‌های تکراری updated_at متفاوت دارند؟ (قاعده‌ی پشتیبان پیشنهادی) -- اگر n_ties > 0 یعنی updated_at هم تفکیک نمی‌کند SELECT 'groups_where_updated_at_ties' AS metric, COUNT(*) AS n FROM ( SELECT mobile, COUNT(*) AS c, COUNT(DISTINCT updated_at) AS d FROM customers WHERE deleted_at IS NULL AND mobile IS NOT NULL AND mobile <> '' GROUP BY mobile HAVING COUNT(*) > 1 AND COUNT(DISTINCT updated_at) < COUNT(*) ) t; -- آیا ردیف‌های تکراری بدهی دارند؟ ادغامشان یعنی جمع‌کردن بدهی SELECT 'dup_groups_with_debt' AS metric, COUNT(*) AS n FROM ( SELECT mobile FROM customers WHERE deleted_at IS NULL AND mobile IS NOT NULL AND mobile <> '' GROUP BY mobile HAVING COUNT(*) > 1 AND (SUM(COALESCE(remain,0)) <> 0 OR SUM(COALESCE(rial_remain,0)) <> 0) ) t; -- آیا ردیف‌های تکراری سفارش دارند؟ SELECT 'dup_rows_with_orders' AS metric, COUNT(DISTINCT c.id) AS n FROM customers c JOIN orders o ON o.customer_id = c.id AND o.deleted_at IS NULL WHERE c.deleted_at IS NULL AND c.mobile IN ( SELECT mobile FROM customers WHERE deleted_at IS NULL AND mobile IS NOT NULL AND mobile <> '' GROUP BY mobile HAVING COUNT(*) > 1 ); -- --------------------------------------------------------------------------- -- سؤال ۳ — نگاشت تصویر محصول: سه ستون → چهار نقش ProductImageRole -- -- مرجع: §۹ — file_id / mobile_pic_id / indicator_id_id -- --------------------------------------------------------------------------- SELECT '=== 3. PRODUCT IMAGE ROLES ===' AS section; SELECT 'file_id (FEATURED?)' AS col, COUNT(file_id) AS not_null FROM product WHERE deleted_at IS NULL UNION ALL SELECT 'mobile_pic_id (FEATURED_MOBILE?)', COUNT(mobile_pic_id) FROM product WHERE deleted_at IS NULL UNION ALL SELECT 'indicator_id_id (?)', COUNT(indicator_id_id) FROM product WHERE deleted_at IS NULL; -- products_productimage همان GALLERY است؟ چند تصویر per محصول؟ SELECT 'gallery_rows' AS metric, COUNT(*) AS n FROM products_productimage WHERE deleted_at IS NULL UNION ALL SELECT 'gallery_distinct_products', COUNT(DISTINCT product_id) FROM products_productimage WHERE deleted_at IS NULL UNION ALL SELECT 'gallery_orphan_image', SUM(i.id IS NULL) FROM products_productimage pi LEFT JOIN image i ON i.id = pi.image_id WHERE pi.deleted_at IS NULL UNION ALL SELECT 'gallery_orphan_product', SUM(p.id IS NULL) FROM products_productimage pi LEFT JOIN product p ON p.id = pi.product_id WHERE pi.deleted_at IS NULL; -- ستون w در products_productimage — آیا نقش را تفکیک می‌کند؟ SELECT 'prod_image.w' AS col, COALESCE(w,'(null)') AS val, COUNT(*) AS n FROM products_productimage WHERE deleted_at IS NULL GROUP BY w ORDER BY n DESC LIMIT 10; -- یتیم‌های تصویر محصول (file_id → image) SELECT 'product.file_id orphan' AS ref, COUNT(*) AS orphans FROM product p LEFT JOIN image i ON i.id = p.file_id WHERE p.deleted_at IS NULL AND p.file_id IS NOT NULL AND i.id IS NULL UNION ALL SELECT 'product.mobile_pic_id orphan', COUNT(*) FROM product p LEFT JOIN image i ON i.id = p.mobile_pic_id WHERE p.deleted_at IS NULL AND p.mobile_pic_id IS NOT NULL AND i.id IS NULL; -- --------------------------------------------------------------------------- -- سؤال ۴ — کارشناس فروش (type=1): ۵۶ ردیف که به AdminUser می‌روند -- مرجع: §۵.۷ · D17 -- --------------------------------------------------------------------------- SELECT '=== 4. SALES EXPERTS (type=1 -> AdminUser) ===' AS section; SELECT 'experts_total' AS metric, COUNT(*) AS n FROM customers WHERE deleted_at IS NULL AND type = 1 UNION ALL SELECT 'experts_with_password', SUM(password IS NOT NULL AND password <> '') FROM customers WHERE deleted_at IS NULL AND type = 1 UNION ALL SELECT 'experts_with_ncode', SUM(ncode IS NOT NULL AND ncode <> '') FROM customers WHERE deleted_at IS NULL AND type = 1 UNION ALL SELECT 'experts_with_city', SUM(city_id IS NOT NULL) FROM customers WHERE deleted_at IS NULL AND type = 1 UNION ALL SELECT 'experts_with_percent', SUM(percent IS NOT NULL AND percent <> 0) FROM customers WHERE deleted_at IS NULL AND type = 1 UNION ALL SELECT 'experts_referenced_as_reagent', COUNT(DISTINCT reagent_id) FROM customers WHERE deleted_at IS NULL AND reagent_id IS NOT NULL; -- 🔴 حیاتی: آیا reagent_id واقعاً به customers اشاره می‌کند؟ (§۵.۷) -- اگر orphan_in_customers صفر یا کم باشد → تأیید می‌شود SELECT 'reagent_id orphan vs customers' AS ref, COUNT(*) AS orphans FROM customers c LEFT JOIN customers r ON r.id = c.reagent_id WHERE c.deleted_at IS NULL AND c.reagent_id IS NOT NULL AND r.id IS NULL; -- آیا همه‌ی reagent_id ها به ردیف type=1 اشاره می‌کنند؟ SELECT 'reagent points to non-expert' AS ref, COUNT(*) AS n FROM customers c JOIN customers r ON r.id = c.reagent_id WHERE c.deleted_at IS NULL AND r.type <> 1; SELECT '=== END ===' AS section;