2013-03-19 19 views
6

... pivot (sum (A) do B w (x))Dynamiczny pivot w Oracle SQL

Teraz B jest od typu danych varchar2 i X jest ciągiem wartości VARCHAR2 oddzielonych przecinkami.
Wartości dla X to zaznacz różne wartości z kolumny (powiedz CL) tej samej tabeli. W ten sposób działało zapytanie przestawne.

Ale problemem jest to, że gdy pojawia się nowa wartość w kolumnie CL muszę ręcznie dodać, że do X. strun

Próbowałem zastępując X z wybranymi odrębne wartości z CL. Ale zapytanie nie działa.
Powodem, dla którego czułem, było to, że do zastąpienia X potrzebujemy wartości oddzielonych przecinkami.
Następnie utworzyłem funkcję zwracającą dokładne dane wyjściowe w celu dopasowania do ciągu X. Ale zapytanie nadal nie działa.
Wyświetlane komunikaty o błędach są podobne do "brakujące pary righrowe", "koniec kanału komunikacji z plikami" itp.
Wypróbowałem xml przestawny zamiast tylko pivot, zapytanie uruchamia się, ale daje znaki takie jak oraxxx itd., Które nie mają żadnych wartości .

Może nie używam go poprawnie.
Czy możesz mi powiedzieć jakąś metodę tworzenia wartości przestawnej z wartościami dynamicznymi?

Odpowiedz

6

Nie można umieścić stałego ciągu w klauzuli IN klauzuli.
Możesz użyć do tego celu Pivot XML.

Od documentation:

subquery A subquery is used only in conjunction with the XML keyword. When you specify a subquery, all values found by the subquery are used for pivoting

Powinno to wyglądać tak:

select xmlserialize(content t.B_XML) from t_aa 
pivot xml(
sum(A) for B in(any) 
) t; 

Można również mieć podzapytanie zamiast słowa kluczowego ANY:

select xmlserialize(content t.B_XML) from t_aa 
pivot xml(
sum(A) for B in (select cl from t_bb) 
) t; 

Here is a sqlfiddle demo

+0

hi metoda działa, ale faktycznie wyjście otrzymuję w formacie XML. mogę uzyskać dane wyjściowe jako tabelę z wierszami i kolumnami? – prabhakar

+0

AFAIK, nie dynamicznie ... Ale jak zamierzasz użyć wyniku, którego nie znasz? –

15

Nie można umieścić instrukcji dynamicznej w instrukcji IN instrukcji PIVOT bez użycia PIVOT XML, który wyprowadza niektóre mniej pożądane wyniki. Możesz jednak utworzyć ciąg IN i wprowadzić go do swojego wyciągu.

Po pierwsze, oto moja próbka tabeli;

myNumber myValue myLetter 
---------- ---------- -------- 
     1   2 A   
     1   4 B   
     2   6 C   
     2   8 A   
     2   10 B   
     3   12 C   
     3   14 A  

Najpierw skonfiguruj ciąg znaków do użycia w instrukcji IN. Tutaj umieszczasz ciąg w "str_in_statement". Używamy COLUMN NEW_VALUE i LISTAGG do ustawienia ciągu znaków.

clear columns 
COLUMN temp_in_statement new_value str_in_statement 
SELECT DISTINCT 
    LISTAGG('''' || myLetter || ''' AS ' || myLetter,',') 
     WITHIN GROUP (ORDER BY myLetter) AS temp_in_statement 
    FROM (SELECT DISTINCT myLetter FROM myTable); 

ciąg będzie wyglądać następująco:

'A' AS A,'B' AS B,'C' AS C 

Teraz użyć instrukcji String w zapytaniu PIVOT.

SELECT * FROM 
    (SELECT myNumber, myLetter, myValue FROM myTable) 
    PIVOT (Sum(myValue) AS val FOR myLetter IN (&str_in_statement)); 

Oto wyjściowa:

MYNUMBER  A_VAL  B_VAL  C_VAL 
---------- ---------- ---------- ---------- 
     1   2   4    
     2   8   10   6 
     3   14     12 

Istnieją ograniczenia chociaż. Można łączyć tylko ciąg do 4000 bajtów.

+0

podczas próby tego otrzymuję poniżej błędu oracle: ORA-56900: zmienna wiązania nie jest obsługiwana wewnątrz operacji pivot | unpivot – lourdh

+0

Jak zaimplementować to w procedurze Oracle? pls podać przykład – Ram

1

użyłem powyższej metody (Anton PL/SQL funkcja zwyczaj pivot()) i zrobić to zadanie ! Ponieważ nie jestem profesjonalnym programistą Oracle, to są to proste kroki, które zrobiłem:

1) Pobierz pakiet zip, aby znaleźć tam plik pivotFun.sql. 2) Uruchom raz pivotFun.sql, aby utworzyć nową funkcję 3) Użyj funkcji w normalnym SQL.

Uważaj tylko na dynamiczne nazwy kolumn. W moim środowisku okazało się, że nazwa kolumny jest ograniczona do 30 znaków i nie może zawierać pojedynczego cudzysłowu. Tak, moje zapytanie jest teraz coś takiego:

SELECT 
    * 
FROM 
    table( 
     pivot(' 
       SELECT DISTINCT 
        P.proj_id, 
        REPLACE(substr(T.UDF_TYPE_LABEL, 1, 30), '''''''','','') as Attribute, 
        CASE 
         WHEN V.udf_text is null  and V.udf_date is null and  V.udf_number is NOT null THEN to_char(V.udf_number) 
         WHEN V.udf_text is null  and V.udf_date is NOT null and V.udf_number is null  THEN to_char(V.udf_date) 
         WHEN V.udf_text is NOT null and V.udf_date is null and  V.udf_number is null  THEN V.udf_text 
         ELSE NULL END 
        AS VALUE 
       FROM 
        project P 
       LEFT JOIN UDFVALUE V ON P.proj_id  = V.proj_id 
       LEFT JOIN UDFTYPE T ON V.UDF_TYPE_ID = T.UDF_TYPE_ID 
       WHERE 
        P.delete_session_id IS NULL AND 
        T.TABLE_NAME = ''PROJECT'' 
    ') 
) 

Działa dobrze z zapisami do 1m.

0

Nie zamierzam udzielać odpowiedzi na pytanie, które zadał OP, zamiast tego będę tylko opisywał, w jaki sposób można przeprowadzić dynamiczny węzeł obrotu.

Tutaj musimy użyć dynamicznego sql, początkowo pobierając wartości kolumn do zmiennej i przekazując zmienną wewnątrz dynamicznego sql.

Przykład

Rozważmy mamy tabelę jak poniżej.

enter image description here

Jeśli musimy pokazać wartości w kolumnie YR jako nazwy kolumn i wartości w tych kolumnach od QTY, to możemy użyć poniższego kodu.

declare 
    sqlqry clob; 
    cols clob; 
begin 
    select listagg('''' || YR || ''' as "' || YR || '"', ',') within group (order by YR) 
    into cols 
    from (select distinct YR from EMPLOYEE); 


    sqlqry := 
    '  
    select * from 
    (
     select * 
     from EMPLOYEE 
) 
    pivot 
    (
    MIN(QTY) for YR in (' || cols || ') 
)'; 

    execute immediate sqlqry; 
end; 
/

WYNIK

enter image description here

W razie potrzeby można również utworzyć tabelę temp i zrobić kwerendę wybierającą w tej tabeli temp, aby zobaczyć wyniki. To proste, po prostu dodaj kod CREATE TABLE TABLENAME AS w powyższym kodzie.

sqlqry := 
'  
    CREATE TABLE TABLENAME AS 
    select * from 
0

używać dynamicznych ZAPYTANIE

kod testowy jest poniżej


-- DDL for Table TMP_TEST 
-------------------------------------------------------- 

    CREATE TABLE "TMP_TEST" 
    ( "NAME" VARCHAR2(20), 
    "APP" VARCHAR2(20) 
    ); 
/
SET DEFINE OFF; 
Insert into TMP_TEST (NAME,APP) values ('suhaib','2'); 
Insert into TMP_TEST (NAME,APP) values ('suhaib','1'); 
Insert into TMP_TEST (NAME,APP) values ('shahzad','3'); 
Insert into TMP_TEST (NAME,APP) values ('shahzad','2'); 
Insert into TMP_TEST (NAME,APP) values ('shahzad','5'); 
Insert into TMP_TEST (NAME,APP) values ('tariq','1'); 
Insert into TMP_TEST (NAME,APP) values ('tariq','2'); 
Insert into TMP_TEST (NAME,APP) values ('tariq','6'); 
Insert into TMP_TEST (NAME,APP) values ('tariq','4'); 
/
    CREATE TABLE "TMP_TESTAPP" 
    ( "APP" VARCHAR2(20) 
    ); 

SET DEFINE OFF; 
Insert into TMP_TESTAPP (APP) values ('1'); 
Insert into TMP_TESTAPP (APP) values ('2'); 
Insert into TMP_TESTAPP (APP) values ('3'); 
Insert into TMP_TESTAPP (APP) values ('4'); 
Insert into TMP_TESTAPP (APP) values ('5'); 
Insert into TMP_TESTAPP (APP) values ('6'); 
/
create or replace PROCEDURE temp_test(
    pcursor out sys_refcursor, 
    PRESULT     OUT VARCHAR2 
    ) 
AS 
V_VALUES VARCHAR2(4000); 
V_QUERY VARCHAR2(4000); 
BEGIN 
PRESULT := 'Nothing'; 

-- concating activities name using comma, replace "'" with "''" because we will use it in dynamic query so "'" can effect query. 
    SELECT DISTINCT 
     LISTAGG('''' || REPLACE(APP,'''','''''') || '''',',') 
     WITHIN GROUP (ORDER BY APP) AS temp_in_statement 
    INTO V_VALUES 
    FROM (SELECT DISTINCT APP 
      FROM TMP_TESTAPP); 

-- designing dynamic query 

    V_QUERY := 'select * 
       from ( select NAME,APP 
          from TMP_TEST ) 
       pivot (count(*) for APP in 
        (' ||V_VALUES|| ')) 
      order by NAME' ; 

    OPEN PCURSOR 
    FOR V_QUERY; 


PRESULT := 'Success'; 

Exception 
WHEN OTHERS THEN 
PRESULT := SQLcode || ' - ' || SQLERRM; 
END temp_test;