4

顧客のテーブルがあるとします。

CREATE TABLE customers (
    customer_number  INTEGER,
    customer_name    VARCHAR(...),
    customer_address VARCHAR(...)
)

このテーブルには主キーがありません。ただし、customer_nameandは任意の に対して一意であるcustomer_address 必要customer_numberがあります。

このテーブルに多くの重複した顧客が含まれることは珍しくありません。この重複を回避するために、次のクエリを使用して一意の顧客のみを分離します。

SELECT
  DISTINCT customer_number, customer_name, customer_address
FROM customers

幸いなことに、テーブルには従来から正確なデータが含まれていました。つまり、競合するcustomer_nameorがあったことは一度もありませcustomer_addresscustomer_number。ただし、競合するデータがテーブルに入ったとします。問題の複数の行を返すのではなく、失敗するクエリを作成したいと考えていますcustomer_number

たとえば、次のクエリを試してみましたが成功しませんでした。

SELECT
  customer_number, DISTINCT(customer_name, customer_address)
FROM customers
GROUP BY customer_number

標準 SQL を使用してそのようなクエリを作成する方法はありますか? そうでない場合、Oracle 固有の SQL に解決策はありますか?

編集:奇妙なクエリの背後にある理論的根拠:

正直なところ、この customers テーブルは実際には存在しません (ありがたいことに)。クエリのニーズを示すのに十分明確になることを期待して作成しました。ただし、その例に基づいて、そのようなクエリの必要性が私の心配の中で最も少ないことに人々は (幸いなことに) 気づいています。したがって、私は抽象化の一部を剥がして、このようなテーブルの忌まわしさを示唆しているという私の評判を取り戻さなければなりません...

外部システムから請求書 (1 行に 1 通) を含むフラット ファイルを受け取りました。このファイルを行ごとに読み取り、そのフィールドをこのテーブルに挿入します。

CREATE TABLE unprocessed_invoices (
    invoice_number   INTEGER,
    invoice_date     DATE,
    ...
    // other invoice columns
    ...
    customer_number  INTEGER,
    customer_name    VARCHAR(...),
    customer_address VARCHAR(...)
)

ご覧のとおり、外部システムから到着するデータは非正規化されています。つまり、外部システムには、請求書データとそれに関連する顧客データの両方が同じ行に含まれています。複数の請求書が同じ顧客を共有する可能性があるため、顧客データが重複する可能性があります。

すべての顧客がシステムに登録されていることが保証されるまで、システムは請求書の処理を開始できません。したがって、システムは一意の顧客を識別し、必要に応じて登録する必要があります。これが、クエリが必要な理由です。非正規化されたデータを操作していたため、制御できませんでした

SELECT
  customer_number, DISTINCT(customer_name, customer_address)
FROM unprocessed_invoices
GROUP BY customer_number

これが質問の本来の意図を明確にするのに役立つことを願っています。

編集: 良い/悪いデータの例

明確にするために:customer_name特定の に対してcustomer_address一意である必要があるだけです。customer_number

 customer_number | customer_name | customer_address
----------------------------------------------------
 1               | 'Bob'         | '123 Street'
 1               | 'Bob'         | '123 Street'
 2               | 'Bob'         | '123 Street'
 2               | 'Bob'         | '123 Street'
 3               | 'Fred'        | '456 Avenue'
 3               | 'Fred'        | '789 Crescent'

最初の 2 行は同じで 1 であるため問題ありcustomer_nameませcustomer_addresscustomer_number

中央の 2 つの行は、同じcustomer_nameand customer_addressfor customer_number2 であるため問題ありません (別の行customer_numberには同じcustomer_nameandがありますがcustomer_address)。

3には 2 つの異なるesがあるため、最後の 2 行は問題ありませんcustomer_addresscustomer_number

私が探しているクエリは、これらの 6 つの行すべてに対して実行すると失敗します。ただし、最初の 4 行のみが実際に存在する場合、ビューは次を返す必要があります。

 customer_number | customer_name | customer_address
----------------------------------------------------
 1               | 'Bob'         | '123 Street'
 2               | 'Bob'         | '123 Street'

customer_nameこれで、「競合する」という言葉の意味が明確になることを願っていcustomer_addressます。ごとに一意である必要がありますcustomer_number

外部システムからデータを適切にインポートする方法を説明している人に感謝します。実際、私はすでにそのほとんどをすでに行っています。目の前の質問に集中しやすくするために、私がやっていることのすべての詳細を意図的に隠しました。このクエリは、検証の唯一の形式ではありません。私はそれが素晴らしい仕上げになると思っただけです(いわば最後の防御)。この質問は、SQL で可能なことを調査するために作成されたものです。:)

4

8 に答える 8

2

スカラー サブクエリは、(結果セットの行ごとに...) 1 つの行のみを返す必要があるため、次のようなことができます。

個別に選択
       顧客番号、
       (
       個別に選択
              customer_address
         お客様から c2
        c2.customer_number = c.customer_number
       ) customer_address として
  お客様から
于 2009-06-12T18:15:50.647 に答える
0

クエリを失敗させるのは難しいかもしれません...

これにより、テーブルに重複するレコードがあるかどうかが表示されます。

select customer_number, customer_name, customer_address
from customers
group by customer_number, customer_name, customer_address
having count(*) > 1

3つのフィールドすべてに一意のインデックスを追加するだけでは、テーブルに重複するレコードを作成することはできません。

于 2009-06-12T17:44:02.407 に答える
0

デフォルトのキーは Name+Address であるため、グループ化する必要があります。

SELECT
  Customer_Name,
  Customer_Address,
  CASE WHEN Count(DISTINCT Customer_Number) > 1
    THEN 1/0 ELSE 0 END as LandMine
FROM Customers
GROUP BY Customer_Name, Customer_Address

Customer_Number の観点からそれを行いたい場合は、これも適切です。

SELECT *, 
CASE WHEN Exists((
  SELECT top 1 1
  FROM Customers c2
  WHERE c1.Customer_Number != c2.Customer_Number
    AND c1.Customer_Name = c2.Customer_Name
    AND c1.Customer_Address = c2.Customer_Address
)) THEN 1/0 ELSE 0 END as LandMine
FROM Customers c1
WHERE Customer_Number = @Number
于 2009-06-12T17:53:23.150 に答える
0

失敗させたい場合は、インデックスが必要になります。インデックスを作成したくない場合は、一時テーブルを作成して、これをすべて行うことができます。

CREATE TABLE #temp_customers 
    (customer_number int, 
    customer_name varchar(50), 
    customer_address varchar(50),
    PRIMARY KEY (customer_number),
     UNIQUE(customr_name, customer_address))

)

INSERT INTO #temp_customers
SELECT DISTINCT customer_number, customer_name, customer_address
FROM customers

SELECT customer_number, customer_name, customer_address
FROM #temp_customers

DROP TABLE #temp_customers

問題がある場合、これは失敗しますが、重複レコードが問題を引き起こすことはありません。

于 2009-06-12T17:55:50.883 に答える