سؤال

حصلت على c#.net التطبيق التي تحتاج إلى تعديل.الاستعلام في لحظة فعالية هذا:

select * from contract where contractnum = :ContractNum

(مبسط جدا, فقط لإظهار باستخدام = معلمة واحدة)

أن المعلمة هي قراءة من الإعدادات.إعدادات الملف في C# التطبيق لديه سلسلة واحدة في ذلك.أنا بحاجة إلى تعديل لتشمل عدة عقود ، حتى يمكنني تغيير SQL إلى:

select * from contract where contractnum in (:ContractNum)

ولكن إرجاع أية نتائج ، بغض النظر عن كيف شكل سلسلة في المعلمة.

هل هناك طريقة يمكنني الحصول على أوراكل القيام مع معلمة ؟

أي عن تقديره للمساعدة شكرا جميعا.

هل كانت مفيدة؟

المحلول

وحتى الآن لم تجد ديسيبل التي تدعم تقييم متغير سلسلة واحدة تحتوي على الفواصل لفصل كما شرط IN الوحيد.

وخياراتك هي سلاسل فرعية متغير حتى بفواصل الفاصلة يتم تشغيل محتويات متغير في صفوف، بحيث يمكنك ثم الانضمام إلى هذا. أو استخدام SQL الديناميكي، الذي هو عبارة SQL التي شيدت كسلسلة في sproc قبل تنفيذ بيان.

نصائح أخرى

هل يمكن استخدام وظيفة عبر خط انابيب لتحويل سلسلة إلى الجدول الذي يمكن استخدامه مع المشغل IN. على سبيل المثال (اختبار مع 10gR2):

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

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

ومع الحزمة التالية:

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

وأن الاستعلام الخاص بك تبدو مثل هذا:

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

ويمكنك استخدام مجموعة أوراكل للأرقام كمعلمة (ربط متغير) عند استخدام ODP.NET كما dataprovider. هذا يعمل مع خادم أوراكل 9، 10 أو 11، والإفراج ODP.net> = 11.1.0.6.20.

وحل مماثل محتمل عند استخدام dataprovider. NET Devart لأوراكل.

ودعونا تحديد العقود مع contractnum في 3 و 4.

وعلينا أن استخدام نوع أوراكل لنقل مجموعة من الأرقام عقدا لدينا الاستعلام.

ويستخدم MDSYS.SDO_ELEM_INFO_ARRAY لأننا إذا استخدام هذا النوع أوراكل مسبقا بالفعل نحن لم يكن لديك لتحديد لدينا نوع أوراكل الخاصة. يمكنك ملء MDSYS.SDO_ELEM_INFO_ARRAY مع 1048576 أرقام كحد أقصى.

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();
    }
  }
}

لا تستخدم المؤشر على contract.contractnum عندما يغفل احد تلميح / * + أصل (علامة التبويب 10) * /. توليت contractnum هو المفتاح الأساسي لذلك سيتم فهرستها هذا العمود.

وانظر أيضا هنا: http://forums.oracle.com/ المنتديات / thread.jspa؟ messageID = 3869879 # 3869879

لاستخدام المعلمة مع بيان IN يمكنك استخدام هذا البناء:

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

وحيث ContractNum هو نوع مجموعة مخصصة.

أعرف أن هذا هو مسألة قديمة لكنها واحدة من عدة فيها اختيار إجابة لم تحل مشكلتي و لا تريد أن تبدأ بعد آخر موضوع على هذا الموضوع لذا سأضع ما وجدت في رحلاتي على أمل أنها قد تساعد شخص.

أنا لا أعمل مع أوراكل كثيرا ولكن مثل SQL Server ، يبدو أن لتمرير قيم الجدول المعلمة تحتاج إلى أن يكون المقابل UDT (يحددها المستخدم الجدول) التي لديك أذونات التنفيذ (قد أكون مخطئا).وهذا يعني أن إجابات أخرى تقترح استخدام المدمج في SYS UDT تأتي مع بعض البضائع و لم أتمكن من معرفة ما إذا كان من الممكن حقا أن تمر جدول شيء غير PL/SQL الإجراء المخزن في النسخة الحالية من ODP.net.

ثانيا السلسلة-تحليل الحل هو حل أهوج لمشكلة لجميع أسباب واضحة (لا ذاكرة التخزين المؤقت خطة التنفيذ أو أيا كان أوراكل المكالمات ، لا مقياس جيد ، إلخ).

لذلك قضيت كثيرا من الوقت في محاولة القيام في البند باستخدام قيم الجدول المعلمة على datamart التي قرأت فقط إذن قبل أن المسببة للعمى فلاش واضحة (في ASP.net المنتدى لا أقل).تبين أوراكل يدعم Xml الاستفسارات 'أصلا لذا بدلا من تمرير صفيف من القيم التي يمكن أن تمر xml قائمة (إذا كان هذا هو كل ما تحتاجه).مرة أخرى, قد أكون مخطئا, لكن يتم التعامل معها باعتبارها شرعية تربط المعلمة و هذا مثال على كيف بسيط هو أن استخدام (vb.net, ADO.net, ODP.net باستخدام حزمة 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()

هذا هو أكثر من ملاحظة من بحثها بعناية حل لذا يرجى التعليق إذا كان هناك أي شيء غير لائق عن القيام بهذه الطريقة.

تحرير.ويبدو أن هناك 4000 حرف يحد من استخدام هذه الطريقة (2000 إذا NVARCHAR) لذلك اضطررت إلى ساعتي الترحيل.الإعلامية رسالة خطأ تحصل عليه إذا كنت تذهب أكثر 'أورا-01460:غير منفذة أو غير معقول التحويل المطلوبة'

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top