專案需求:比對 copyright_songs(版權曲庫)與 local_songs(本地使用曲庫),找出重複歌曲(交集)與無版權歌曲(差集),並處理歌名含括號、編號為 NULL 等髒資料問題。
| 表名 | 關鍵欄位 |
|---|---|
| copyright_songs | id, name, singer, company, type |
| local_songs | id, name, singer, company |
SELECT DISTINCT
t2.id AS copyright_id,
t1.name, t1.singer,
t1.id AS local_id,
t1.company AS local_company,
t2.company AS copyright_company,
t2.type
INTO same_1
FROM copyright_songs t2
INNER JOIN local_songs t1
ON t1.name = t2.name AND t1.singer = t2.singer;
-- 備份原表
SELECT * INTO local_songs_old FROM local_songs;
-- 移除中文括號及其後內容
UPDATE local_songs
SET name = LEFT(name, CHARINDEX('(', name) - 1)
WHERE CHARINDEX('(', name) > 0;
-- 重新比對交集
DROP TABLE same_1;
SELECT DISTINCT
t2.id, t1.name, t1.singer, t1.id AS local_id,
t1.company, t2.company AS copyright_company, t2.type
INTO same_1
FROM copyright_songs t2
INNER JOIN local_songs t1
ON t1.name = t2.name AND t1.singer = t2.singer;
SELECT DISTINCT
t1.id, t2.name AS original_name, t1.singer,
t1.local_id, t1.local_company, t1.copyright_company, t1.type
FROM same_1 t1
INNER JOIN local_songs_old t2 ON t1.local_id = t2.id;
-- 檢查 NULL
SELECT * FROM local_songs WHERE id IS NULL;
SELECT * FROM same_1 WHERE local_id IS NULL;
-- 清除
DELETE FROM local_songs WHERE id IS NULL;
DELETE FROM same_1 WHERE local_id IS NULL;
SELECT *
INTO no_local_songs
FROM local_songs
WHERE id NOT IN (SELECT local_id FROM same_1);
-- 檢視結果
SELECT * FROM no_local_songs;
| 操作 | SQL 模式 | 適用場景 | 注意事項 |
|---|---|---|---|
| 交集 | INNER JOIN / INTERSECT (2005+) | 雙邊鍵值完全匹配 | 需處理資料清洗(大小寫、空白、標點) |
| 差集 (A-B) | NOT IN / NOT EXISTS / LEFT JOIN ... WHERE IS NULL / EXCEPT (2005+) | 找出僅存在於左表的行 | NOT IN 遇 NULL 會回空集,優先用 NOT EXISTS |
| 聯集 | UNION ALL (保留重複) / UNION (去重) | 合併同結構結果集 | 大量資料用 UNION ALL 避免排序 |
normalized_name 計算欄位(移除標點、全形轉半形、統一大小寫)並建索引,避免查詢時即時函數運算。SOUNDEX、DIFFERENCE 或全文檢索語意比對。NOT IN 子查詢在大資料量下易退化為巢狀迴圈,改寫為 NOT EXISTS 或 LEFT JOIN 通常更穩定。local_songs_old 等中間表,支援人工複核與爭議處理。查出的 no_local_songs(無版權歌曲)需立即下架/屏蔽,避免使用免費網路音樂牟利引發法律風險。建議建立定期自動化比對排程(每日/每週),新增版權曲庫時自動同步釋放。