Pergunta

Got um c # .net aplicativo que eu preciso modificar. A consulta no momento efetivamente faz isso:

select * from contract where contractnum = :ContractNum

(muito simplificado, só para mostrar que estamos usando um = e um parâmetro)

Esse parâmetro é lido a partir do arquivo Settings.Settings no aplicativo C # e tem uma corda nele. Eu preciso modificá-lo para incluir vários contratos, então eu acho que eu posso mudar o SQL para:

select * from contract where contractnum in (:ContractNum)

mas que não retorna nenhum resultado, não importa como eu formatar a cadeia no parâmetro.

Existe uma maneira eu posso obter oráculo para fazer um com um parâmetro?

qualquer ajuda apreciada, obrigado todos.

Foi útil?

Solução

ainda têm de encontrar um db que suportes avaliar uma variável de cadeia única contendo vírgulas para separar como a cláusula IN sola.

As suas opções são a subsequência a variável de modo a vírgula delimitado o conteúdo de variáveis ??são transformados em fileiras, para que você possa, em seguida, juntar-se para isso. Ou, para usar SQL dinâmico, que é uma instrução SQL construído como uma corda em um sproc antes a instrução é executada.

Outras dicas

você poderia usar uma função pipeline para transformar uma string em uma mesa que poderia ser usada com o operador IN. Por exemplo (testado com 10gR2):

SQL> select * from table(demo_pkg.string_to_tab('i,j,k'));

COLUMN_VALUE
-----------------
i
j
k

com o seguinte pacote:

SQL> CREATE OR REPLACE PACKAGE demo_pkg IS
  2     TYPE varchar_tab IS TABLE OF VARCHAR2(4000);
  3     FUNCTION string_to_tab(p_string VARCHAR2,
  4                            p_delimiter VARCHAR2 DEFAULT ',')
  5        RETURN varchar_tab PIPELINED;
  6  END demo_pkg;
  7  /

Package created
SQL> CREATE OR REPLACE PACKAGE BODY demo_pkg IS
  2     FUNCTION string_to_tab(p_string VARCHAR2,
  3                            p_delimiter VARCHAR2 DEFAULT ',')
  4        RETURN varchar_tab PIPELINED IS
  5        l_string          VARCHAR2(4000) := p_string;
  6        l_first_delimiter NUMBER := instr(p_string, p_delimiter);
  7     BEGIN
  8        LOOP
  9           IF nvl(l_first_delimiter,0) = 0 THEN
 10              PIPE ROW(l_string);
 11              RETURN;
 12           END IF;
 13           PIPE ROW(substr(l_string, 1, l_first_delimiter - 1));
 14           l_string          := substr(l_string, l_first_delimiter + 1);
 15           l_first_delimiter := instr(l_string, p_delimiter);
 16        END LOOP;
 17     END;
 18  END demo_pkg;
 19  /

Package body created

A sua consulta seria algo como isto:

select * 
  from contract 
 where contractnum in (select column_value
                         from table(demo_pkg.string_to_tab(:ContractNum)))

Você pode usar uma coleção Oráculo de números como um parâmetro (variável de ligação) quando você usa ODP.NET como dataprovider. Isso funciona com servidor Oracle 9, 10 ou 11 e ODP.net release> = 11.1.0.6.20.

Uma solução semelhante é possível quando você usa Devart do .NET dataprovider para Oracle.

Vamos selecionar os contratos com o contractnum 3 e 4.

Temos que usar um tipo de Oracle para transferir uma matriz de números de contrato para a nossa consulta.

MDSYS.SDO_ELEM_INFO_ARRAY é usado porque se usarmos este Oracle já predefinidos tipo que não tem que definir o nosso próprio tipo Oracle. Você pode preencher MDSYS.SDO_ELEM_INFO_ARRAY com Max 1048576 números.

using Oracle.DataAccess.Client;
using Oracle.DataAccess.Types;

[OracleCustomTypeMappingAttribute("MDSYS.SDO_ELEM_INFO_ARRAY")]
public class NumberArrayFactory : IOracleArrayTypeFactory
{
  public Array CreateArray(int numElems)
  {
    return new Decimal[numElems];
  }

  public Array CreateStatusArray(int numElems)
  {
    return null;
  }
}

private void Test()
{
  OracleConnectionStringBuilder b = new OracleConnectionStringBuilder();
  b.UserID = "sna";
  b.Password = "sna";
  b.DataSource = "ora11";
  using (OracleConnection conn = new OracleConnection(b.ToString()))
  {
    conn.Open();
    using (OracleCommand comm = conn.CreateCommand())
    {
      comm.CommandText =
      @" select  /*+ cardinality(tab 10) */ c.*  " +
      @" from contract c, table(:1) tab " +
      @" where c.contractnum = tab.column_value";

      OracleParameter p = new OracleParameter();
      p.OracleDbType = OracleDbType.Array;
      p.Direction = ParameterDirection.Input;
      p.UdtTypeName = "MDSYS.SDO_ELEM_INFO_ARRAY";
      //select contract 3 and 4
      p.Value = new Decimal[] { 3, 4 };
      comm.Parameters.Add(p);

      int numContracts = 0;
      using (OracleDataReader reader = comm.ExecuteReader())
      {
        while (reader.Read())
        {
           numContracts++;
        }
      }
      conn.Close();
    }
  }
}

O índice em contract.contractnum não é usado quando um omite dica / * + cardinalidade (aba 10) * /. Presumi contractnum é a chave principal para esta coluna serão indexados.

Veja também aqui: http://forums.oracle.com/ fóruns / thread.jspa? messageID = 3869879 # 3869879

Para usar o parâmetro com a declaração IN você pode usar esta construção:

select * from contract where contractnum
in (select column_value from table (:ContractNum))

onde ContractNum é o tipo de matriz personalizado.

Eu sei que isto é uma questão antiga, mas é um dos vários em que a resposta selecionada não resolver o meu problema e eu não quero começar ainda outra discussão sobre este tema, então eu vou colocar para baixo o que eu encontrei em minhas viagens na esperança de que ele pode ajudar alguém.

Eu não trabalho com a Oracle muito, mas, como em SQL Server, parece que para passar um parâmetro que você precisa ter um UDT (usuário tabela definida) correspondente para o qual você tem permissão para executar valor de tabela (eu poderia ser errado). Isto significa que outras respostas que sugerem o uso de um built-in SYS UDT vêm com algum frete e eu não conseguia descobrir se realmente é possível passar uma tabela para algo que não é um PL / SQL procedimento armazenado na versão atual de ODP.net.

Em segundo lugar, a solução cadeia de análise é um truque para todas as razões óbvias (não pode armazenar em cache o plano de execução ou o que a Oracle chama, não escala bem, etc).

Então eu passei um pouco muito tempo tentando fazer o IN-cláusula usando um parâmetro com valor de tabela em um datamart para que eu só ter permissão de leitura antes de eu foi atingido por um clarão ofuscante do óbvio ( Em nenhum menos um fórum ASP.net ). Acontece que a Oracle oferece suporte a consultas XML 'nativamente' Então, ao invés de passar uma matriz de valores você pode passar uma lista XML (se isso é tudo que você precisa). Mais uma vez, eu posso estar errado, mas ele é tratado como um parâmetro de ligação legítimo e este é um exemplo de como é simples de usar (vb.net, ADO.net, ODP.net usando pacote NuGet):

    Dim xe As New XElement("l", New XElement("i", "ITEM-A"), New XElement("i", "ITEM-B"))
    Using conn As New OracleConnection(myConnectionString)
        conn.Open()
        Using cmd As OracleCommand = conn.CreateCommand()
            cmd.CommandType = CommandType.Text
            Dim query As String
            query = "  SELECT s.FOO, q.BAR " & vbCrLf
            query &= " FROM TABLE1 s LEFT OUTER JOIN " & vbCrLf
            query &= "      TABLE2 q ON q.ID = s.ID " & vbCrLf
            query &= " WHERE (COALESCE(q.ID, 'NULL') NOT LIKE '%OPTIONAL%') AND "
            query &= "       (s.ID IN ("
            query &= "                      SELECT stid "
            query &= "                      FROM XMLTable('/l/i' PASSING XMLTYPE(:stid) COLUMNS stid VARCHAR(32) PATH '.')"
            query &= "                 )"
            query &= "        )"
            cmd.CommandText = query
            Dim parameter As OracleParameter = cmd.Parameters.Add("stid", OracleDbType.NVarchar2, 4000)
            parameter.Value = xe.ToString
            Using r As OracleDataReader = cmd.ExecuteReader
                While r.Read()
                    //Do something
                End While
            End Using
        End Using
        conn.Close()

Esta é mais uma observação do que uma solução cuidadosamente estudadas para comentário, por favor se há alguma coisa inadequada sobre fazê-lo desta forma.

EDIT. Há aparentemente um limite de 4000 caracteres usando este método (2000 se NVARCHAR) então eu tive que assistir a minha paginação. A mensagem de erro informativo você ganha se você passar por cima é 'ORA-01460: por implementar ou conversão razoável solicitado'

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top