Как перебирать строку, не полученную из таблицы, или просмотр PL / SQL? [Дубликат]

Это не прямой ответ, а пример того, как CRTP может быть полезен.


Хороший конкретный пример CRTP является std::enable_shared_from_this из C ++ 11:

[util.smartptr.enab] / 1

Класс T может наследуем от enable_­shared_­from_­this, чтобы наследовать функции shared_­from_­this-члена, которые получают экземпляр shared_­ptr, указывающий на *this.

То есть, наследуя от std::enable_shared_from_this, можно получить общий (или слабый) указатель на ваш экземпляр без доступа к нему (например, из функции-члена, где вы знаете только о *this).

Это полезно, когда вам нужно дать std::shared_ptr, но вы имеют доступ только к *this:

struct Node;

void process_node(const std::shared_ptr &);

struct Node : std::enable_shared_from_this // CRTP
{
    std::weak_ptr parent;
    std::vector> children;

    void add_child(std::shared_ptr child)
    {
        process_node(shared_from_this()); // Shouldn't pass `this` directly.
        child->parent = weak_from_this(); // Ditto.
        children.push_back(std::move(child));
    }
};

Причина, по которой вы не можете просто передать this прямо вместо shared_from_this(), заключается в том, что она нарушит механизм владения:

struct S
{
    std::shared_ptr get_shared() const { return std::shared_ptr(this); }
};

// Both shared_ptr think they're the only owner of S.
// This invokes UB (double-free).
std::shared_ptr s1 = std::make_shared();
std::shared_ptr s2 = s1->get_shared();
assert(s2.use_count() == 1);
]

7
задан Tom H 29 June 2010 в 19:00
поделиться

5 ответов

Следующие работы ссылаются на него как на выбор * из таблицы (разделитель ('a, b, c, d'))

create or replace function splitter(p_str in varchar2) return  sys.odcivarchar2list
is
v_tab sys.odcivarchar2list:=new sys.odcivarchar2list();
begin
with cte as (select level  ind from dual
connect by 
level <=regexp_count(p_str,',') +1
)
select regexp_substr(p_str,'[^,]+',1,ind)
bulk collect into v_tab
from cte;
return v_tab;
end;
/
10
ответ дан josephj1989 20 August 2018 в 22:09
поделиться

Увы, в 11g нам все равно придется использовать наши собственные токенизаторы PL / SQL, используя типы SQL. В 11gR2 Oracle предоставила нам агрегирующую функцию для объединения результатов в строку CSV, поэтому, возможно, в 12i они предоставят обратную возможность.

Если вы не хотите создавать тип SQL, вы можете использовать встроенный SYS.DBMS_DEBUG_VC2COLL, например:

create or replace function string_tokenizer
    (p_string in varchar2
        , p_separator in varchar2 := ',')
    return sys.dbms_debug_vc2coll
is
    return_value SYS.DBMS_DEBUG_VC2COLL;
    pattern varchar2(250);
begin

    pattern := '[^('''||p_separator||''')]+' ;

    select trim(regexp_substr (p_string, pattern, 1, level)) token
    bulk collect into return_value
    from dual
    where regexp_substr (p_string, pattern, 1, level) is not null
    connect by regexp_instr (p_string, pattern, 1, level) > 0;

    return return_value;

end string_tokenizer;
/

Здесь он находится в действии:

SQL> select * from table (string_tokenizer('one, two, three'))
  2  /

COLUMN_VALUE
----------------------------------------------------------------
one
two
three

SQL>

Подтверждение: этот код является вариантом кода I найденный в блоге Tanel Poder .

4
ответ дан APC 20 August 2018 в 22:09
поделиться

Для оптимальной производительности лучше избегать использования иерархических запросов (CONNECT BY) в функции сплиттера.

Следующая функция сплиттера значительно улучшает работу с большими объемами данных

CREATE OR REPLACE FUNCTION row2col(p_clob_text IN VARCHAR2) 
   RETURN sys.dbms_debug_vc2coll PIPELINED 
IS
     next_new_line_indx PLS_INTEGER;
     remaining_text VARCHAR2(20000);
     next_piece_for_piping VARCHAR2(20000);
  BEGIN

    remaining_text := p_clob_text;
    LOOP
       next_new_line_indx := instr(remaining_text, ','); 
       next_piece_for_piping :=
          CASE
             WHEN next_new_line_indx <> 0 THEN
                TRIM(SUBSTR(remaining_text, 1, next_new_line_indx-1))
             ELSE
                TRIM(SUBSTR(remaining_text, 1))
          END;

       remaining_text := SUBSTR(remaining_text, next_new_line_indx+1 );
       PIPE ROW(next_piece_for_piping);
       EXIT WHEN next_new_line_indx = 0 OR remaining_text IS NULL;
    END LOOP;
    RETURN;
  END row2col;
/

Эта разница в производительности может наблюдаться ниже (я использовал сплиттер функции, как было указано ранее в этом обсуждении).

SQL> SET TIMING ON
SQL>
SQL> WITH SRC AS (
  2  SELECT rownum||',a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z'||rownum txt
  3  FROM DUAL
  4  CONNECT BY LEVEL <=10000
  5  )
  6  SELECT  NULL
  7  FROM SRC, TABLE(SYSTEM.row2col(txt)) t
  8  HAVING MAX(t.column_value) > 'zzz'
  9  ;

no rows selected

Elapsed: 00:00:00.93
SQL>
SQL> WITH SRC AS (
  2  SELECT rownum||',a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z'||rownum txt
  3  FROM DUAL
  4  CONNECT BY LEVEL <=10000
  5  )
  6  SELECT  NULL
  7  FROM SRC, TABLE(splitter(txt)) t
  8  HAVING MAX(t.column_value) > 'zzz'
  9  ;

no rows selected

Elapsed: 00:00:14.90
SQL>
SQL> SET TIMING OFF
SQL>
2
ответ дан Bobby Kenny 20 August 2018 в 22:09
поделиться

У меня не установлен 11g, но есть операция PIVOT и UNPIVOT для преобразования столбцов в строки / строки в столбцы, что может быть хорошей отправной точкой.

http://www.oracle.com/technology/pub/articles/oracle-database-11g-top-features/11g-pivot.html

(Фактически проведенное некоторое дальнейшее исследование, это не подходит для этого случая - оно работает с фактическими строками / столбцами, но не с наборами данных в столбце).

Существует также DBMS_UTILITY.comma_to_table и table_to_comma для преобразования списков CSV в pl / sql-таблицы. Есть некоторые ограничения (обработка строк и т. Д.), Но может быть хорошей отправной точкой.

Моей наклонностью было бы использовать подход TYPE с простой функцией, которая выполняет comma_to_table, затем PIPE ROW для каждой записи в результат comma_to_table (к сожалению, DBMS_UTILITY.comma_to_table - это процедура, поэтому нельзя звонить из SQL).

0
ответ дан JulesLt 20 August 2018 в 22:09
поделиться

Вот еще одно решение, использующее регулярное выражение, полностью в sql.

SELECT regexp_substr('one,two,three','[^,]+', 1, level) abc
FROM dual 
CONNECT BY regexp_substr('one,two,three', '[^,]+', 1, level) IS NOT NULL
3
ответ дан Shailesh 20 August 2018 в 22:09
поделиться
Другие вопросы по тегам:

Похожие вопросы: