回答

收藏

通过SQL查询安全规范化数据

技术问答 技术问答 246 人阅读 | 0 人回复 | 2023-09-13

假设我有一个客户表:; a! V) r& \, D% ?
CREATE TABLE customers (, F6 n8 \8 z+ ?2 A$ f; ~
    customer_number  INTEGER,) q1 a7 V8 X: V0 B5 T. M
    customer_name    VARCHAR(...),
  \* o% B  e$ D6 A7 C" K/ V    customer_address VARCHAR(...)0 i7 W9 I8 b0 @# j
)
7 u# z7 M. v5 Q此表也 没有 主键。但是,customer_name并且对于任何给定都customer_address 应该* q* f2 M9 S! ?. x3 h/ O0 N0 a7 w
是唯一的customer_number。
! p0 L2 |$ b5 o! |+ T$ j$ p, M该表包含许多重复的客户并不少见。为了避免重复,使用以下查询仅隔离唯一的客户:1 D7 U) I) b/ {# a- Z2 ^
SELECT
3 \6 E( n' F- R- Y+ P  DISTINCT customer_number, customer_name, customer_address3 M: g" i( P; `7 H" S$ _
FROM customers2 |" @% c, a3 }& h
幸运的是,该表格传统上包含准确的数据。也就是说,从来没有任何冲突customer_name或customer_address任何冲突customer_number。但是,假设有冲突的数据确实将其放入表中。我希望编写一个将失败的查询,而不是为有customer_number问题的查询返回多行。, a: b7 M( I  |* |
例如,我尝试此查询没有成功:0 Y- v- p4 Q: @9 N. p8 D% C
SELECT
, q4 f  r9 n0 X2 M  customer_number, DISTINCT(customer_name, customer_address)7 a. C( L: ~/ w& e
FROM customers3 @$ v* ]# \6 c1 C$ ?" m
GROUP BY customer_number
7 _3 }. w* J3 \' U6 I( W# ]& Z  O有没有办法使用标准SQL编写这样的查询?如果没有,在Oracle特定的SQL中是否有解决方案?
' V( k9 a* n1 B5 Y8 k: k6 S& }+ _编辑:离奇查询背后的原理:1 }+ E2 \$ b5 n2 \
说实话,这个客户表实际上并不存在(谢天谢地)。我创建它的目的是希望它足够清楚以演示查询的需求。但是,基于该示例,人们(幸运地)意识到对这种查询的需求是我的最不担心的事情。因此,我现在必须删除一些抽象,并希望恢复我对建议使用此类可憎表的声誉。$ q' `- T& \( Y; _' S; P0 e1 `
我从外部系统收到包含发票(每行一张)的平面文件。我逐行读取了此文件,并将其字段插入此表中:
" r9 v  X& \  Z) i4 c# y* TCREATE TABLE unprocessed_invoices (
, }# x1 K! |# r. q% [) E    invoice_number   INTEGER,/ _: e* H  Q1 z" v2 Q7 W  E
    invoice_date     DATE,2 P% ~, B# ~- W' A( u- m$ N, O
    ...$ }0 B3 E2 u+ n" O% k4 K
    // other invoice columns+ e- a% B3 G3 j
    ...
. d* S% Y4 v' |+ M    customer_number  INTEGER,& @! a5 V$ s- r
    customer_name    VARCHAR(...),
+ R8 H  x# C8 t- K' R" I    customer_address VARCHAR(...)
$ a; c2 B% b% w' e2 B)# R9 K5 f9 v8 P
如您所见,来自外部系统的数据已被非规范化。即,外部系统在同一行上同时包含发票数据及其关联的客户数据。多个发票可能共享同一客户,因此可能有重复的客户数据。& K2 T, E5 K( ]. Y/ g
在保证所有客户都已在系统中注册之前,系统无法开始处理发票。因此,系统必须识别唯一的客户并在必要时对其进行注册。这就是为什么我要查询的 原因0 y2 p! F' l8 t
因为我正在使用非规范化数据,所以我无法控制
/ f  T  k6 N! ?) K7 X* u4 tSELECT# ^8 m# q% J: o; z0 }
  customer_number, DISTINCT(customer_name, customer_address)
7 S7 s- J8 z# w1 k) [FROM unprocessed_invoices1 X4 U  ]0 d7 ~  n
GROUP BY customer_number
8 n5 y/ o0 [7 i" i0 z希望这有助于阐明问题的初衷。
5 N- u+ j3 i8 F编辑:好/坏数据的示例
- l* m* @1 A4 U' k  q# ]: y要澄清:customer_name并且customer_address仅必须 针对特定的customer_number唯一。+ L3 T) V2 ]& o! A+ r9 g' I1 v) }
customer_number | customer_name | customer_address( _) f/ a. D- g; W, ~1 X- y3 n
----------------------------------------------------. s, D. r4 g! d+ t% f. `7 k2 ~+ e
1               | 'Bob'         | '123 Street'% J: P, r2 c% n9 K3 ^0 G
1               | 'Bob'         | '123 Street'2 }( e5 k7 T% X. }0 X
2               | 'Bob'         | '123 Street'
/ U5 H' \; x! ]6 @8 U( n9 m, _ 2               | 'Bob'         | '123 Street'
! v; y5 U3 W$ C5 v. c4 S2 l5 y' { 3               | 'Fred'        | '456 Avenue'7 b2 f) s/ t, u$ Z" d% g. q) x
3               | 'Fred'        | '789 Crescent'
7 A- s3 q- M; G6 K% U6 ^( F前两排是很好,因为它是相同的customer_name,并customer_address为customer_number1。) t+ ]& g1 t' h% ^# A) U6 g
中间两排是很好,因为它是相同的customer_name,并customer_address为customer_number2(即使另一个customer_number具有相同的customer_name和customer_address)。
# A- {, G# `0 x最后两行 不正确, 因为3有两个不同的customer_addresses customer_number。7 j4 p- x2 l! Q  B+ B3 W' d
如果针对所有这六行运行,我正在寻找的查询将失败。但是,如果仅实际存在前四行,则该视图应返回:7 Q' k: D* T6 L% d
customer_number | customer_name | customer_address8 Y! J: d: k' n  D' Q8 C
----------------------------------------------------
3 A8 F- w8 G5 ^ 1               | 'Bob'         | '123 Street'
+ X, b' @  U! `) h* k# `' w 2               | 'Bob'         | '123 Street'  X; j/ z. |. F4 V! J
我希望这可以澄清“冲突customer_name和customer_address”的含义。它们必须是唯一的customer_number。
) t& P3 n2 X9 q2 D. S# o我感谢那些正在解释如何正确地从外部系统导入数据的人。 实际上,我已经在做大多数事情了。我 特意
' J9 n3 z) s2 U+ i; i2 D0 Y隐藏了我正在做的所有细节,以便更轻松地专注于手头的问题。此查询并非唯一的验证形式。我只是认为这会给人留下很好的画龙点睛(可以说是最后一道防线)。这个问题只是为了调查SQL可能发生的情况而设计。
( S3 b4 E  [- S+ q" V6 \7 Y                # p& d2 _* w6 y* J5 z. @
解决方案:6 j) A0 `1 `6 I3 n; {2 Y
                - i8 r, Z* l2 t% L

& K& F8 K6 V+ H* Z0 _  J
. ]6 |2 j, i, y- q: v                标量子查询只能返回一行(每个结果集行…),因此您可以执行以下操作:
' n% k" S: e0 c, r1 d: D" Dselect distinct
, f; l' {0 O9 M, V9 |       customer_number,
5 Z0 I: U; s% U/ O7 w0 `+ ^       (  T+ I+ y$ s4 p6 Q( o6 r$ L4 _! a
       select distinct
# y; x/ x( {: S+ z7 Y4 y# W- I              customer_address
$ O! o/ P3 H/ ]/ l: Q6 y* D3 z         from customers c2; J) I* U) ?2 \1 `( G# u
        where c2.customer_number = c.customer_number
4 V$ J; O3 e2 P. M* l! _$ s       ) as customer_address
  M0 I" r! G1 K( }  from customers c
分享到:
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则