2010-12-11 2 views
4

group by 절에 고유 한 식을 추가하는 경우가 종종 있습니다. 때로는 잘못 되었기 때문에 SQL 또는 잘못된 가정에 오류가 있으며, 그 표현은 정말 고유하지 않습니다.하나의 고유 입력 만 허용하는 집계 함수

내 결과 세트를 조용히, 때로는 매우 미묘하게 확장하지 않고 SQL 오류를 생성하는 경우가 많습니다.

select product_id, unique description from product group by product_id 

을하지만, 분명히 나 자신 있음을 구현할 수 없습니다 -하지만 뭔가 거의 간결 일부 데이터베이스에서 사용자 정의 집계로 구현 될 수있다

내가 뭔가를 할 수 싶어요.

고유 한 입력 값만 허용하는 특수 집계는 일반적으로 모든 버전의 SQL에서 유용합니까? 그렇다면 대부분의 데이터베이스에서 그런 것을 구현할 수 있습니까? null 값은 다른 값과 마찬가지로 고려해야합니다. 기본 제공 집계 avg이 일반적으로 작동하는 방식과 달리. (나는 포스트 그레스와 오라클을 위해 이것을 구현하는 방법으로 해답을 추가했다.)

다음 예는 집계가 어떻게 사용되는지 보여주기위한 것이지만, 어떤 표현식이 유일해야하는지 간단하게 보여주는 간단한 경우이다.

product_id | description 
------------+------------- 
      1 | anvil 
      2 | brick 
      3 | clay 
      4 | door 

sale_id | product_id | cost 
---------+------------+--------- 
     1 |   1 | £100.00 
     2 |   1 | £101.00 
     3 |   1 | £102.00 
     4 |   2 | £3.00 
     5 |   2 | £3.00 
     6 |   2 | £3.00 
     7 |   3 | £24.00 
     8 |   3 | £25.00 

쿼리 :

> select * from product join sale using (product_id); 

product_id | description | sale_id | cost 
------------+-------------+---------+--------- 
      1 | anvil  |  1 | £100.00 
      1 | anvil  |  2 | £101.00 
      1 | anvil  |  3 | £102.00 
      2 | brick  |  4 | £3.00 
      2 | brick  |  5 | £3.00 
      2 | brick  |  6 | £3.00 
      3 | clay  |  7 | £24.00 
      3 | clay  |  8 | £25.00 

> select product_id, description, sum(cost) 
    from product join sale using (product_id) 
    group by product_id, description; 

product_id | description | sum 
------------+-------------+--------- 
      2 | brick  | £9.00 
      1 | anvil  | £303.00 
      3 | clay  | £49.00 

> select product_id, solo(description), sum(cost) 
    from product join sale using (product_id) 
    group by product_id; 

product_id | solo | sum 
------------+-------+--------- 
      1 | anvil | £303.00 
      3 | clay | £49.00 
      2 | brick | £9.00 

오류의 경우 :

> select solo(description) from product; 
ERROR: This aggregate only allows one unique input 
+0

, MySQL의 – XMen

+0

@Rahul에있는 솔로 기능이 있습니다 이 일반적인 SQL 질문 - 나는 익숙하지 않은 데이터베이스 (postgres 및 Oracle) –

답변

3
실제 사용은 가능성이 고유성

테이블에 대한 잘못된 가정을하는 것이 더 쉽습니다 큰 쿼리에있을 것입니다

postgres에 대한 구현은 다음과 같습니다 (null을 고유 한 v ALUE도) : 테스트를위한

create function solo_sfunc(inout anyarray, anyelement) 
     language plpgsql immutable as $$ 
begin 
    if $1 is null then 
    $1[1] := $2; 
    else 
    if ($1[1] is not null and $2 is null) 
     or ($1[1] is null and $2 is not null) 
     or ($1[1]!=$2) then 
     raise exception 'This aggregate only allows one unique input'; 
    end if; 
    end if; 
    return; 
end;$$; 

create function solo_ffunc(anyarray) returns anyelement 
     language plpgsql immutable as $$ 
begin 
    return $1[1]; 
end;$$; 

create aggregate solo(anyelement) 
        (sfunc=solo_sfunc, stype=anyarray, ffunc=solo_ffunc); 

예를 들어 테이블 :

create table product(product_id integer primary key, description text); 

insert into product(product_id, description) 
values (1, 'anvil'), (2, 'brick'), (3, 'clay'), (4, 'door'); 

create table sale(sale_id serial primary key, 
        product_id integer not null references product, 
        cost money not null); 

insert into sale(product_id, cost) 
values (1, '100'::money), (1, '101'::money), (1, '102'::money), 
     (2, '3'::money), (2, '3'::money), (2, '3'::money), 
     (3, '24'::money), (3, '25'::money); 
+0

에 대한 답변을 얻으려고합니다. 좋은 함수이지만 입력 유형 (9.5 미만)의 배열을 반환하는 것으로 보입니다. 그것은 정확히 입력 된 형식을 반환 할? – MarHoff

1

당신은 (PRODUCT_ID, 설명)에 UNIQUE 제약 조건을 정의해야합니다, 당신은 하나 개의 제품 약이되는 두 가지 설명을 걱정하지 않아도 .

+1

"실제 사용은 독창성에 대한 잘못된 가정을하는 것이 더 쉬운 큰 쿼리에서 더 많이 발생합니다". '테이블'은 집계가있는 하위 쿼리와 같은 데이터 소스 일 수 있습니다. 고유성을 적용하기 위해 데이터베이스 제약 조건을 추가하기가 쉽지 않습니다. –

+0

어쨌든 product_id가 –

7

오라클 솔루션은

select product_id, 
     case when min(description) != max(description) then to_char(1/0) 
      else min(description) end description, 
     sum(cost) 
    from product join sale using (product_id) 
    group by product_id; 

보다는 DIVIDE_BY_ZERO 오류를 발생시키는 TO_CHAR (1/0)), 당신은

CREATE OR REPLACE FUNCTION solo (i_min IN VARCHAR2, i_max IN VARCHAR2) 
RETURN VARCHAR2 IS 
BEGIN 
    IF i_min != i_max THEN 
    RAISE_APPLICATION_ERROR(-20001, 'Non-unique value specified'); 
    ELSE 
    RETURN i_min; 
    END; 
END; 
/
select product_id, 
     solo(min(description),max(description)) end description, 
     sum(cost) 
from product join sale using (product_id) 
group by product_id; 

당신은 사용할 수 있습니다을하는 간단한 기능을 사용할 수 있습니다 사용자 정의 집계,하지만 SQL과 PL/SQL 사이의 전환의 성능 영향에 대해 걱정할 것입니다.

+0

이기 때문에 어쨌든 (product_id, description)은 고유합니다. 이 독창적 인 접근 방식을 생각하지 않았습니다. SQL을 가능한 간결하게 유지할 수있는 무언가를 찾고 있습니다. 이상적으로는 'select id, product group by product_id'라는 고유 한 구문이 필요합니다.하지만 공급 업체는 추가하지 않는 것이 좋습니다. 내가 생각하는 표준. 또한 게시하려는 오라클 응답에서와 같이 고유 한 값으로 간주되도록 null을 강제하도록 메서드를 변경하는 방법도 생각할 수 없습니다. –

1

그리고 여기에 오라클에 대한 내 구현 - 불행하게도 당신이 각 기본 유형에 대한 하나의 구현을 필요가 있다고 생각 :

당신이 오라클 MySQL을 사용하는 데이터베이스
create type SoloNumberImpl as object 
(
    val number, 
    flag char(1), 
    static function ODCIAggregateInitialize(sctx in out SoloNumberImpl) 
     return number, 
    member function ODCIAggregateIterate(self in out SoloNumberImpl, 
             value in number) 
     return number, 
    member function ODCIAggregateTerminate(self in SoloNumberImpl, 
              returnValue out number, 
              flags in number) 
     return number, 
    member function ODCIAggregateMerge(self in out SoloNumberImpl, 
             ctx2 in SoloNumberImpl) 
     return number 
); 
/

create or replace type body SoloNumberImpl is 
static function ODCIAggregateInitialize(sctx in out SoloNumberImpl) 
     return number is 
begin 
    sctx := SoloNumberImpl(null, 'N'); 
    return ODCIConst.Success; 
end; 

member function ODCIAggregateIterate(self in out SoloNumberImpl, 
             value in number) 
     return number is 
begin 
    if self.flag='N' then 
    self.val:=value; 
    self.flag:='Y'; 
    else 
    if (self.val is null and value is not null) 
     or (self.val is not null and value is null) 
     or (self.val!=value) then 
     raise_application_error(-20001, 
           'This aggregate only allows one unique input'); 
    end if; 
    end if; 
    return ODCIConst.Success; 
end; 

member function ODCIAggregateTerminate(self in SoloNumberImpl, 
             returnValue out number, 
             flags in number) 
     return number is 
begin 
    returnValue := self.val; 
    return ODCIConst.Success; 
end; 

member function ODCIAggregateMerge(self in out SoloNumberImpl, 
            ctx2 in SoloNumberImpl) 
     return number is 
begin 
    if self.flag='N' then 
    self.val:=ctx2.val; 
    self.flag=ctx2.flag; 
    elsif ctx2.flag='Y' then 
    if (self.val is null and ctx2.val is not null) 
      or (self.val is not null and ctx2.val is null) 
      or (self.val!=ctx2.val) then 
     raise_application_error(-20001, 
           'This aggregate only allows one unique input'); 
    end if; 
    end if; 
    return ODCIConst.Success; 
end; 
end; 
/

create function SoloNumber (input number) 
return number aggregate using SoloNumberImpl; 
/
관련 문제