ক্লজ বিকল্পে প্রস্তুত স্টেটমেন্ট?


343

SQL ইনজেকশন আক্রমণ সুরক্ষা সমস্যার কারণে একাধিক মানের জন্য সমর্থিত নয় এমন INউদাহরণগুলির সাথে একটি এসকিউএল ধারা ব্যবহার করার জন্য সর্বোত্তম কাজের ক্ষেত্রগুলি কী java.sql.PreparedStatement: এক ?স্থানধারক মানগুলির তালিকার পরিবর্তে একটি মানকে উপস্থাপন করে।

নিম্নলিখিত এসকিউএল বিবৃতি বিবেচনা করুন:

SELECT my_column FROM my_table where search_column IN (?)

ব্যবহার প্রথমত preparedStatement.setString( 1, "'A', 'B', 'C'" );ব্যবহারের কারণগুলির একটি কার্যক্রমে মূলত একটি অ-কার্যকারী প্রচেষ্টা ?

কোন কাজের ক্ষেত্র উপলব্ধ?


1
অস্কার, আমি মনে করি (?,?, ....) এর গতিশীল প্রজন্মটি যদি আপনার আইএন ক্লজের প্রয়োজন হয় তবে সবচেয়ে সহজ কাজ, তবে আমি আমার ব্যক্তিগত ক্ষেত্রে পারফরম্যান্সই যথেষ্ট ছিল বলে ব্যক্তিগত কলগুলিতে ছেড়ে দিয়েছি।
ক্রিস মাজনোলা

6
প্রস্তুত বিবৃতিগুলির একটি সুবিধা হ'ল দক্ষতার জন্য সোহুল্ড একবার সংকলন করা যায়। ধারাটিকে গতিশীল করে এটি কার্যকরভাবে প্রস্তুত বিবৃতিটিকে অস্বীকার করে।

2
প্রকৃতপক্ষে, এটি মাইএসকিউএল (প্যারামিটার মান হিসাবে স্ট্রিংয়ের একটি অ্যারে সেট করতে setObject ব্যবহার করে) এর জন্য কাজ করে। আপনি কোন ডিবি ব্যবহার করছেন?
ফ্রান্স


এখানে একটি সম্পর্কিত প্রশ্ন: stackoverflow.com/q/6956025/521799
লুকাশ এডার

উত্তর:


194

উপলব্ধ বিভিন্ন বিকল্পের বিশ্লেষণ, এবং প্রতিটিের পক্ষে ভাল এবং কনস এখানে পাওয়া যায়

প্রস্তাবিত বিকল্পগুলি হ'ল:

  • SELECT my_column FROM my_table WHERE search_column = ?প্রতিটি মান এবং ইউনিয়ন ফলাফলের ক্লায়েন্ট-পাশের জন্য এটি প্রস্তুত করুন , কার্যকর করুন। শুধুমাত্র একটি প্রস্তুত বিবৃতি প্রয়োজন। ধীর এবং বেদনাদায়ক।
  • এটি প্রস্তুত SELECT my_column FROM my_table WHERE search_column IN (?,?,?)এবং কার্যকর করুন। তালিকা-আকারের প্রতি একটি প্রস্তুত বিবৃতি প্রয়োজন। দ্রুত এবং সুস্পষ্ট।
  • এটি প্রস্তুত SELECT my_column FROM my_table WHERE search_column = ? ; SELECT my_column FROM my_table WHERE search_column = ? ; ...এবং কার্যকর করুন। [বা UNION ALLসেমিকোলনের জায়গায় ব্যবহার করুন । --ed] -র-তালিকা-তালিকা অনুসারে একটি প্রস্তুত বিবৃতি প্রয়োজন। নির্বোধের চেয়ে ধীরে ধীরে, এর চেয়ে কঠোরতর খারাপ WHERE search_column IN (?,?,?), সুতরাং ব্লগার কেন এটির পরামর্শ দিয়েছিল আমি জানি না।
  • ফলাফল সেট তৈরি করতে একটি সঞ্চিত পদ্ধতি ব্যবহার করুন।
  • এন-তালিকা-তালিকা-সংক্রান্ত বিভিন্ন প্রশ্নের আকার প্রস্তুত করুন; বলুন, 2, 10 এবং 50 টি মান সহ। 6 টি আলাদা মান সহ একটি IN-তালিকা অনুসন্ধান করতে, আকার -10 কোয়েরিটি তৈরি করুন যাতে এটি দেখতে ভাল লাগে SELECT my_column FROM my_table WHERE search_column IN (1,2,3,4,5,6,6,6,6,6)। যে কোনও শালীন সার্ভার কোয়েরি চালানোর আগে সদৃশ মানগুলি অপ্টিমাইজ করবে।

যদিও এই বিকল্পগুলির কোনওটিই দুর্দান্ত নয়।

সদৃশ বিকল্পগুলির সাথে এই জায়গাগুলিতে সদৃশ প্রশ্নের উত্তর দেওয়া হয়েছে, তবুও তাদের কোনওোটাই দুর্দান্ত দুর্দান্ত নয়:

সঠিক উত্তর, আপনি যদি জেডিবিসি 4 এবং একটি সার্ভার সমর্থন করে x = ANY(y)যা ব্যবহার করে তবে তা PreparedStatement.setArrayএখানে বর্ণিত হিসাবে ব্যবহার করতে হবে :

setArrayযদিও IN-তালিকাগুলি দিয়ে কাজ করার কোনও উপায় নেই বলে মনে হচ্ছে ।


কখনও কখনও এসকিউএল স্টেটমেন্টগুলি রানটাইমে লোড হয় (উদাহরণস্বরূপ, কোনও প্রোপার্টি ফাইল থেকে) তবে প্যারামিটারগুলির একটি পরিবর্তনশীল সংখ্যার প্রয়োজন হয়। এই ধরনের ক্ষেত্রে, প্রথমে ক্যোয়ারী সংজ্ঞায়িত করুন:

query=SELECT * FROM table t WHERE t.column IN (?)

এরপরে, ক্যোয়ারী লোড করুন। তারপরে এটি চালুর আগে প্যারামিটারের সংখ্যা নির্ধারণ করুন। একবার প্যারামিটার গণনা জানা হয়ে গেলে চালান:

sql = any( sql, count );

উদাহরণ স্বরূপ:

/**
 * Converts a SQL statement containing exactly one IN clause to an IN clause
 * using multiple comma-delimited parameters.
 *
 * @param sql The SQL statement string with one IN clause.
 * @param params The number of parameters the SQL statement requires.
 * @return The SQL statement with (?) replaced with multiple parameter
 * placeholders.
 */
public static String any(String sql, final int params) {
    // Create a comma-delimited list based on the number of parameters.
    final StringBuilder sb = new StringBuilder(
            new String(new char[params]).replace("\0", "?,")
    );

    // Remove trailing comma.
    sb.setLength(Math.max(sb.length() - 1, 0));

    // For more than 1 parameter, replace the single parameter with
    // multiple parameter placeholders.
    if (sb.length() > 1) {
        sql = sql.replace("(?)", "(" + sb + ")");
    }

    // Return the modified comma-delimited list of parameters.
    return sql;
}

কিছু নির্দিষ্ট ডাটাবেসের জন্য যেখানে জেডিবিসি 4 স্পেসিফিকেশনের মাধ্যমে কোনও অ্যারে পাস করা অসমর্থিত, এই পদ্ধতিটি ধীর গতিটিকে = ?দ্রুত IN (?)ক্লজ শর্তে রূপান্তর করতে সহায়তা করতে পারে, যারপরে anyপদ্ধতিটি কল করে প্রসারিত করা যায় ।


123

পোস্টগ্রিজ এসকিউএল এর সমাধান:

final PreparedStatement statement = connection.prepareStatement(
        "SELECT my_column FROM my_table where search_column = ANY (?)"
);
final String[] values = getValues();
statement.setArray(1, connection.createArrayOf("text", values));
final ResultSet rs = statement.executeQuery();
try {
    while(rs.next()) {
        // do some...
    }
} finally {
    rs.close();
}

অথবা

final PreparedStatement statement = connection.prepareStatement(
        "SELECT my_column FROM my_table " + 
        "where search_column IN (SELECT * FROM unnest(?))"
);
final String[] values = getValues();
statement.setArray(1, connection.createArrayOf("text", values));
final ResultSet rs = statement.executeQuery();
try {
    while(rs.next()) {
        // do some...
    }
} finally {
    rs.close();
}

1
ভাল দেখাচ্ছে। এই কোডের কোন অংশটি পোস্টরেএসকিউএল নির্দিষ্ট? "কোথায় অনুসন্ধান_ কলাম = কোন (?)"? বা কানেকশন.ক্রেটআরআরেওফ? অথবা অন্য কিছু?
ডেভিড পোর্টাবেলা

1
আমি মনে করি এটি পোস্টগ্র্রেএসকিউএল-নির্দিষ্টের চেয়ে বেশি জেডিবিসি 4-নির্দিষ্ট, .createArrayOf()অংশটির কারণে , তবে আমি নিশ্চিত নই যে ব্যবহারকারীর জন্য কঠোর শব্দার্থবিদ্যা Arrayজেডিবিসি স্পেসিফিকেশন দ্বারা সংজ্ঞায়িত হয়েছে।
ল্যাভেলা

3
যদি .createArrayOfনা কাজ করে, আপনি মত বিন্যাস আক্ষরিক আপনার নিজস্ব ম্যানুয়াল সৃষ্টি করতে পারি না String arrayLiteral = "{A,\"B \", C,D}" (নোট যখন সি এই নয় যে "বি" একটি স্থান আছে) এবং তারপর statement.setString(1,arrayLiteral)যেখানে প্রস্তুত বিবৃতি ... IN (SELECT UNNEST(?::VARCHAR[]))বা ... IN (SELECT UNNEST(CAST(? AS VARCHAR[])))। (পিএস: আমি মনে করি না যে এটি নিয়ে ANYকাজ করে SELECT))
ADTC

দুর্দান্ত সমাধান! সত্যিই আমার জন্য দিনটি বাঁচিয়েছে। পূর্ণসংখ্যার অ্যারের জন্য আমি ক্রিয়েআরএফ () এর প্রথম প্যারামিটারে "ইনট" ব্যবহার করেছি এবং এটি দেখতে ভাল লাগছে। যদিও ডকুমেন্টেশনের ভিত্তিতে এই প্রথম পরামিতিটি ডিবি-নির্দিষ্ট উপস্থিত হয় appears
এমমানুয়েল টাজারি

2
এটি পরিষ্কার সমাধান মনে হয়। যদি কেউ এইচএসকিউএলডিবি নির্দিষ্ট সিনট্যাক্সের সন্ধান করে থাকে: আমি এটি ইউএন (UNNEST (?))
অরিয়ানিয়ামাস

19

কোন সহজ উপায় AFAIK। যদি লক্ষ্যটি থাকে স্টেটমেন্ট ক্যাশে রেশিও বেশি রাখা (অর্থাত্ প্রতিটি প্যারামিটার গণনায় প্রতি বিবৃতি তৈরি না করা), আপনি নিম্নলিখিতটি করতে পারেন:

  1. কয়েকটি (যেমন 10) পরামিতি দিয়ে একটি বিবৃতি তৈরি করুন:

    ... কোথায় (?,?,?,?,??,??,?????) ...

  2. সমস্ত বাস্তব পরামিতি বাঁধুন

    setString (1, "foo বিন্যাস"); setString (2, "বার");

  3. বাকিগুলিকে NULL হিসাবে আবদ্ধ করুন

    সেটনুল (3, প্রকারভেদ.উবারচার) ... সেটনুল (10, প্রকার।

NULL কখনই কোনও কিছুর সাথে মেলে না, সুতরাং এটি এসকিউএল পরিকল্পনা নির্মাতা দ্বারা অনুকূলিত হয়ে যায়।

আপনি যখন ডিএও ফাংশনে কোনও তালিকা পাস করেন তখন যুক্তিটি স্বয়ংক্রিয়ভাবে সহজ হয়:

while( i < param.size() ) {
  ps.setString(i+1,param.get(i));
  i++;
}

while( i < MAX_PARAMS ) {
  ps.setNull(i+1,Types.VARCHAR);
  i++;
}

"NULL কখনই কোনও কিছুর সাথে মেলে না" - কোয়েরিতে ডাটাবেসের NULLকোনও NULLমান মেলে ?
ক্রেগ ম্যাককুইন

5
পছন্দ করুন এএনএসআই স্ট্যান্ডার্ড অনুসারে নালও নালার সাথে মেলে না।
দাউদ ইবনে কেরেম

IS নুল কীওয়ার্ডটি ব্যবহার করে আপনি নুলের সাথে মিল করতে পারেন। যুক্ত টেবিলের অস্তিত্ব নেই এমন সারিগুলি সনাক্ত করার একটি দুর্দান্ত উপায় হ'ল IS NULL এর সাথে বাম জয়েন্টকে ব্যবহার করা। 'নির্বাচন করুন.ইউআরএল, বি। ইউআরএল থেকে ট্যাবলেট_এর একটি বামে যোগদানের টেবিল_বি খ_এ_আরএল = বি_ বি.ইউআর যেখানে বি। ইউআরএল নাল' এটি টেবিল এ-এর সমস্ত সারি দেখায় যেগুলির টেবিল বিতে কোনও মিল নেই
জেনস ট্যান্ডস্টেড

3
যদিও এই বিষয়ে সাবধানতা অবলম্বন করুন। NOT INএবং INনালগুলি একইভাবে পরিচালনা করবেন না। এটি চালান এবং দেখুন কী হয়: select 'Matched' as did_it_match where 1 not in (5, null); তারপরে সরান nullএবং যাদুটি দেখুন।
ব্র্যান্ডন

অথবা আপনি পূর্ববর্তী কোনও পরমের মান হিসাবে সমস্ত অতিরিক্ত প্যারাম সেট করতে পারেন। যে কোনও শালীন ডিবি ইঞ্জিন এগুলি ফিল্টার করে ফেলবে। তাই a IN (1,2,3,3,3,3,3)হিসাবে একই a IN (1,2,3)। এটি NOT INঅসদৃশ সাথেও কাজ করে a NOT IN (1,2,3,null,null,null,null)(যা any_value != NULLসর্বদা মিথ্যা হিসাবে সর্বদা কোনও সারি দেয় না )।
রুসলান স্টেলমাচেঙ্কো

11

একটি অপ্রীতিকর পরিশ্রমী, তবে অবশ্যই নেস্টেড কোয়েরি ব্যবহার করা সম্ভব। এতে একটি কলাম সহ অস্থায়ী টেবিল MYVALUES তৈরি করুন। MYVALUES টেবিলটিতে আপনার মানগুলির তালিকা প্রবেশ করান। তারপরে মৃত্যুদণ্ড কার্যকর করুন

select my_column from my_table where search_column in ( SELECT value FROM MYVALUES )

কুরুচিপূর্ণ, তবে একটি কার্যকর বিকল্প যদি আপনার মানগুলির তালিকা খুব বড় হয়।

এই কৌশলটিতে অপ্টিমাইজার থেকে সম্ভাব্য আরও ভাল ক্যোয়ারী পরিকল্পনাগুলির সংযুক্তি রয়েছে (একাধিক মানের জন্য একটি পৃষ্ঠা পরীক্ষা করুন, প্রতি মানের পরিবর্তে কেবল একবার টেবিলস্ক্যান ইত্যাদি) যদি আপনার ডাটাবেস প্রস্তুত বিবৃতি ক্যাশে না করে তবে ওভারহেডে সংরক্ষণ করতে পারে। আপনার "INSERTS" ব্যাচে সম্পন্ন করা দরকার এবং ন্যূনতম লকিং বা অন্যান্য উচ্চ-ওভারহেড সুরক্ষা পেতে MYVALUES টেবিলটি টুইট করতে হবে।


একসাথে আমার_সামগ্রীটির একটি মান জিজ্ঞাসা করার চেয়ে কী কী সুবিধা থাকতে পারে?
পল টমবলিন

3
ক্যোয়ারী অপ্টিমাইজার লোড হওয়া পৃষ্ঠা থেকে সমস্ত সম্ভাব্য ম্যাচগুলি পুনরুদ্ধার করে আই / ও লোড হ্রাস করতে পারে। টেবিলস্ক্যানস বা সূচক স্ক্যানগুলি একবারে প্রতি মানের পরিবর্তে একবার সম্পাদন করা যেতে পারে। সন্নিবেশ মানগুলির জন্য ওভারহেড ব্যাচ ক্রিয়াকলাপের সাথে হ্রাস করা যেতে পারে এবং বেশ কয়েকটি প্রশ্নের তুলনায় কম হতে পারে।
জেমস শেক

1
এটি দেখতে দুর্দান্ত লাগছে, তবে একযোগে সমস্যা থাকতে পারে। জেডিবিসি স্পেসিফিকেশন মেমোরিতে একটি টেম্পোরাল বেনাম টেবিল তৈরি করার উপায় রাখে? বা এর মতো কিছু, সম্ভব হলে জেডিবিসি-বিক্রেতা নির্দিষ্ট নয়?
ডেভিড পোর্টাবেলা

9

ইন () অপারেটরের সীমাবদ্ধতা হ'ল সমস্ত অশুভের মূল।

এটি তুচ্ছ ঘটনাগুলির জন্য কাজ করে এবং আপনি এটি "প্রস্তুত বিবৃতিটির স্বয়ংক্রিয় প্রজন্ম" দিয়ে প্রসারিত করতে পারেন তবে এটি সর্বদা এর সীমাবদ্ধতা রাখে।

  • আপনি যদি প্যারামিটারের পরিবর্তনশীল সংখ্যার সাথে একটি বিবৃতি তৈরি করে থাকেন তবে এটি প্রতিটি কলে একটি স্কয়ার পার্স ওভারহেড করে দেবে
  • অনেক প্ল্যাটফর্মে, () অপারেটরের পরামিতিগুলির সংখ্যা সীমিত
  • সমস্ত প্ল্যাটফর্মে, মোট এসকিউএল পাঠ্যের আকার সীমাবদ্ধ, এতে প্যারামগুলির জন্য 2000 স্থানধারক পাঠানো অসম্ভব হয়ে পড়ে
  • জেডিবিসি ড্রাইভারের সীমাবদ্ধতা থাকায় 1000-10k এর বাইন্ড ভেরিয়েবলগুলি পাঠানো সম্ভব নয়

মধ্যে () পদ্ধতির কিছু ক্ষেত্রে যথেষ্ট ভাল হতে পারে, কিন্তু রকেট প্রমাণ নয় :)

রকেট-প্রুফ সমাধানটি হ'ল পৃথক কলটিতে পরামিতিগুলির স্বেচ্ছাসেবী সংখ্যার পাস (উদাহরণস্বরূপ প্যারামগুলির একটি ক্লোব পাস করে), এবং তারপরে এসকিউএলে তাদের উপস্থাপন করার জন্য একটি ভিউ (বা অন্য কোনও উপায়) থাকতে হবে এবং আপনার যেখানে ব্যবহার করতে হবে নির্ণায়ক.

একটি নিষ্ঠুর-বলের বৈকল্পিক এখানে রয়েছে http://tkyte.blogspot.hu/2006/06/varying-in-lists.html

তবে আপনি যদি PL / SQL ব্যবহার করতে পারেন তবে এই জগাখিচুড়ি বেশ ঝরঝরে হয়ে উঠতে পারে।

function getCustomers(in_customerIdList clob) return sys_refcursor is 
begin
    aux_in_list.parse(in_customerIdList);
    open res for
        select * 
        from   customer c,
               in_list v
        where  c.customer_id=v.token;
    return res;
end;

তারপরে আপনি প্যারামিটারে কমা বিচ্ছিন্ন গ্রাহক আইডির স্বেচ্ছাসেবী পাস করতে পারেন এবং:

  • নির্বাচনের জন্য এসকিউএল স্থিতিশীল হওয়ায় কোনও পার্স বিলম্ব পাবেন না
  • কোনও পাইপলাইনযুক্ত ফাংশনগুলির জটিলতা নেই - এটি কেবল একটি কোয়েরি
  • এসকিউএল একটি আইএন অপারেটরের পরিবর্তে একটি সরল জোড় ব্যবহার করছে, যা বেশ দ্রুত
  • সর্বোপরি, কোনও সাদামাটা সিলেক্ট বা ডিএমএল দিয়ে ডাটাবেস না মারার এটি একটি ভাল নিয়ম , যেহেতু এটি ওরাকল, যা মাইএসকিউএল বা অনুরূপ সাধারণ ডাটাবেস ইঞ্জিনের চেয়েও বেশি আলোকসজ্জার প্রস্তাব করে। পিএল / এসকিউএল আপনাকে কার্যকরভাবে আপনার অ্যাপ্লিকেশন ডোমেন মডেল থেকে স্টোরেজ মডেলটি আড়াল করতে দেয়।

কৌশলটি এখানে:

  • আমাদের একটি কল প্রয়োজন যা দীর্ঘ স্ট্রিং গ্রহণ করে, এবং ডিবি সেশনে এটি অ্যাক্সেস করতে পারে এমন কোথাও সঞ্চয় করে (যেমন সাধারণ প্যাকেজ ভেরিয়েবল, বা dbms_session.set_context)
  • তারপরে আমাদের এমন একটি ভিউ দরকার যা এটি সারিগুলিতে পার্স করতে পারে
  • এবং তারপরে আপনার একটি ভিউ রয়েছে যার মধ্যে আপনি জিজ্ঞাসা করছেন এমন আইডিসমূহ রয়েছে, তাই আপনার যা প্রয়োজন তা সন্ধানের টেবিলের সাথে একটি সরল যোগদান।

মতামতটি দেখে মনে হচ্ছে:

create or replace view in_list
as
select
    trim( substr (txt,
          instr (txt, ',', 1, level  ) + 1,
          instr (txt, ',', 1, level+1)
             - instr (txt, ',', 1, level) -1 ) ) as token
    from (select ','||aux_in_list.getpayload||',' txt from dual)
connect by level <= length(aux_in_list.getpayload)-length(replace(aux_in_list.getpayload,',',''))+1

যেখানে aux_in_list.getpayload মূল ইনপুট স্ট্রিংকে বোঝায়।


সম্ভাব্য পন্থাটি হবে pl / sql অ্যারে (কেবল ওরাকল দ্বারা সমর্থিত) পাস করার জন্য, তবে আপনি খাঁটি এসকিউএল এগুলি ব্যবহার করতে পারবেন না, সুতরাং একটি রূপান্তর পদক্ষেপ সর্বদা প্রয়োজন। রূপান্তরটি এসকিউএলে করা যায় না, সুতরাং সর্বোপরি স্ট্রিংয়ের সমস্ত পরামিতিগুলির সাথে একটি চক্কর উত্তীর্ণ করা এবং এটির মতামত রূপান্তরিত করা সবচেয়ে কার্যকর সমাধান solution


6

আমি নিজের প্রয়োগটিতে এটি কীভাবে সমাধান করেছি তা এখানে। আদর্শভাবে, আপনার স্ট্রিংয়ের জন্য + ব্যবহারের পরিবর্তে একটি স্ট্রিংবিল্ডার ব্যবহার করা উচিত।

    String inParenthesis = "(?";
    for(int i = 1;i < myList.size();i++) {
      inParenthesis += ", ?";
    }
    inParenthesis += ")";

    try(PreparedStatement statement = SQLite.connection.prepareStatement(
        String.format("UPDATE table SET value='WINNER' WHERE startTime=? AND name=? AND traderIdx=? AND someValue IN %s", inParenthesis))) {
      int x = 1;
      statement.setLong(x++, race.startTime);
      statement.setString(x++, race.name);
      statement.setInt(x++, traderIdx);

      for(String str : race.betFair.winners) {
        statement.setString(x++, str);
      }

      int effected = statement.executeUpdate();
    }

কংক্রিট সংখ্যার পরিবর্তে উপরের মতো এক্সের মতো ভেরিয়েবল ব্যবহার করা যদি আপনি পরবর্তী সময়ে ক্যোয়ারী পরিবর্তন করার সিদ্ধান্ত নেন তবে অনেক সহায়তা করে।


একই চেষ্টা করেছিলেন, ওরাকল
সন্তোষ রবিতেজা

5

আমি কখনই চেষ্টা করে দেখিনি, তবে .setArray () আপনি যা খুঁজছেন তা কি করবেন?

আপডেট : স্পষ্টত না। সেটআরে কেবল java.sql.Array এর সাথে কাজ করবে বলে মনে হয় যা আপনি পূর্ববর্তী ক্যোয়ারী, বা একটি আরআরএ কলাম সহ একটি সাবকিউরি থেকে ফিরে আসা একটি অ্যারে কলাম থেকে আসে।


4
সমস্ত ডাটাবেসের সাথে কাজ করে না, তবে এটি "সঠিক" পদ্ধতির।
স্কাফম্যান

আপনি সমস্ত ড্রাইভার বোঝাতে চাইছেন। কিছু ড্রাইভারের এই বছরের পুরানো (গত শতাব্দী?) স্ট্যান্ডার্ডের মালিকানা সমতুল্য রয়েছে। আরেকটি উপায় হ'ল অস্থায়ী টেবিলের মানগুলির একটি ব্যাচকে
বাঁধানো

java.sun.com/j2se/1.3/docs/guide/jdbc/getstart/… সূর্যের মতে, অ্যারের সামগ্রী [সাধারণত] সার্ভারের পাশে থাকে এবং প্রয়োজন অনুসারে টানা হয়। প্রিপেইডস্টেটমেন্ট.সেটআর্রে () ক্লায়েন্টের পাশে নতুন অ্যারে তৈরি না করে পূর্ববর্তী রেজাল্টসেট থেকে কোনও অ্যারে ফিরিয়ে পাঠাতে পারে।
ক্রিস মাজ্জোলা 16 ই

5

আমার কাজটি হ'ল:

create or replace type split_tbl as table of varchar(32767);
/

create or replace function split
(
  p_list varchar2,
  p_del varchar2 := ','
) return split_tbl pipelined
is
  l_idx    pls_integer;
  l_list    varchar2(32767) := p_list;
  l_value    varchar2(32767);
begin
  loop
    l_idx := instr(l_list,p_del);
    if l_idx > 0 then
      pipe row(substr(l_list,1,l_idx-1));
      l_list := substr(l_list,l_idx+length(p_del));
    else
      pipe row(l_list);
      exit;
    end if;
  end loop;
  return;
end split;
/

এখন আপনি একটি টেবিলের কিছু মান পেতে একটি পরিবর্তনশীল ব্যবহার করতে পারেন:

select * from table(split('one,two,three'))
  one
  two
  three

select * from TABLE1 where COL1 in (select * from table(split('value1,value2')))
  value1 AAA
  value2 BBB

সুতরাং, প্রস্তুত বিবৃতি হতে পারে:

  "select * from TABLE where COL in (select * from table(split(?)))"

শুভেচ্ছা সহ,

জাভিয়ের ইবনেজ


এটি হ'ল পিএল / এসকিউএল। এটি অন্যান্য ডাটাবেসে কাজ করবে না। নোট করুন যে এই বাস্তবায়নটির ইনপুট প্যারামগুলির একটি সীমাবদ্ধতা রয়েছে - মোট দৈর্ঘ্য 32k অক্ষরের মধ্যে সীমাবদ্ধ - পাশাপাশি পাইপলাইনযুক্ত ফাংশনে কলটি ওরাকেলের পিএল / এসকিউএল এবং এসকিউএল ইঞ্জিনগুলির মধ্যে একটি প্রসঙ্গ সুইচ তৈরি করার পরে একটি কার্যকারিতা সীমাবদ্ধ করে।
জি বি

3

আমি মনে করি আপনি (বেসিক স্ট্রিং ম্যানিপুলেশন ব্যবহার করে) আপনার তালিকার আইটেমের সংখ্যার সাথে মিলে যাওয়ার জন্য কোয়েরি স্ট্রিংটি তৈরি PreparedStatementকরতে পারেন ?

অবশ্যই যদি আপনি এটি করছেন যে আপনি ORআপনার ক্যোয়ারিতে বেঁধে রাখা একটি দৈত্য তৈরি করা থেকে কেবল এক ধাপ দূরে রয়েছেন ?, তবে ক্যোয়ারী স্ট্রিংয়ের সঠিক নম্বর না থাকলে আপনি কীভাবে অন্যদিকে কাজ করতে পারবেন তা আমি দেখতে পাচ্ছি না।


যেহেতু আমি ভিন্ন সংখ্যায় প্রেরণ করতে চাইছি তখনই আমার জন্য আসলেই কোনও সমাধান নয়? প্রতিবার আমি পিএস কল। তবে ভাববেন না যে আমি এটি বিবেচনা করি নি। : পি
ক্রিস মাজ্জোলা

4
আরেকটি হ্যাক: আপনি প্রচুর পরিমাণে প্যারামিটারের স্থানধারক ব্যবহার করতে পারেন - আপনার কাছে থাকা মানগুলির দীর্ঘতম তালিকা - এবং যদি আপনার মানগুলির তালিকাটি আরও ছোট হয়, আপনি মানগুলি পুনরাবৃত্তি করতে পারেন: ... যেখানে অনুসন্ধানের ক্ষেত্র ইন (? ,?,?,?,?,?,??) এবং তারপরে মানগুলি সরবরাহ করে: এ, বি, সি, ডি, এ, বি, সি, ডি
বিল কারউইন

1
তবে সামগ্রিকভাবে আমি অ্যাডামের সমাধানটির পক্ষে আছি: এসকিউএল গতিশীলভাবে উত্পন্ন করুন, এবং সম্মতিযুক্ত? আপনার পাস করতে হবে এমন সংখ্যার সাথে মেলাতে স্থানধারক।
বিল কারভিন 16 ই

বিল, আমি প্রস্তুতিমূলক স্টেটমেন্টটি পুনরায় ব্যবহার করতে না চাইলে সেই সমাধান কার্যকর হয়। আরেকটি সমাধান হ'ল একক প্যারামকে একাধিকবার কল করা এবং ফলাফল ক্লায়েন্টের পাশে জমা করা। সম্ভবত কাস্টম সংখ্যার সাথে একটি নতুন বিবৃতি তৈরি / সম্পাদন করা আরও দক্ষ হবে? প্রতিটি সময় যদিও।
ক্রিস মাজোলা 16

3

এই জাভাদকটিতে উল্লিখিত হিসাবে আপনি সেটআরাই পদ্ধতিটি ব্যবহার করতে পারেন :

PreparedStatement statement = connection.prepareStatement("Select * from emp where field in (?)");
Array array = statement.getConnection().createArrayOf("VARCHAR", new Object[]{"E1", "E2","E3"});
statement.setArray(1, array);
ResultSet rs = statement.executeQuery();

2
এটি সমস্ত ড্রাইভার দ্বারা সমর্থিত নয়, যদি বৈশিষ্ট্যটি সমর্থন না করা হয় তবে আপনি এসকিউএল
অজানা

দুর্ভাগ্যক্রমে আমার ড্রাইভার এটি সমর্থন করে না
এডএক্সএক্স

এটি ওরাকল
সন্তোষ রবিতেজা

3

আপনি Collections.nCopiesস্থানধারীদের সংগ্রহ উত্পন্ন করতে এবং এগুলি ব্যবহার করে যোগ দিতে পারেন String.join:

List<String> params = getParams();
String placeHolders = String.join(",", Collections.nCopies(params.size(), "?"));
String sql = "select * from your_table where some_column in (" + placeHolders + ")";
try (   Connection connection = getConnection();
        PreparedStatement ps = connection.prepareStatement(sql)) {
    int i = 1;
    for (String param : params) {
        ps.setString(i++, param);
    }
    /*
     * Execute query/do stuff
     */
}

ওরাকল জেডিবিসি ব্যবহার করার সময় এখন পর্যন্ত সেরা সমাধান বলে মনে হচ্ছে ...
jansohn

2

আপনার জন্য প্রস্তুত বিবৃতি তৈরি করার জন্য এখানে জাভায় একটি সম্পূর্ণ সমাধান:

/*usage:

Util u = new Util(500); //500 items per bracket. 
String sqlBefore  = "select * from myTable where (";
List<Integer> values = new ArrayList<Integer>(Arrays.asList(1,2,4,5)); 
string sqlAfter = ") and foo = 'bar'"; 

PreparedStatement ps = u.prepareStatements(sqlBefore, values, sqlAfter, connection, "someId");
*/



import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

public class Util {

    private int numValuesInClause;

    public Util(int numValuesInClause) {
        super();
        this.numValuesInClause = numValuesInClause;
    }

    public int getNumValuesInClause() {
        return numValuesInClause;
    }

    public void setNumValuesInClause(int numValuesInClause) {
        this.numValuesInClause = numValuesInClause;
    }

    /** Split a given list into a list of lists for the given size of numValuesInClause*/
    public List<List<Integer>> splitList(
            List<Integer> values) {


        List<List<Integer>> newList = new ArrayList<List<Integer>>(); 
        while (values.size() > numValuesInClause) {
            List<Integer> sublist = values.subList(0,numValuesInClause);
            List<Integer> values2 = values.subList(numValuesInClause, values.size());   
            values = values2; 

            newList.add( sublist);
        }
        newList.add(values);

        return newList;
    }

    /**
     * Generates a series of split out in clause statements. 
     * @param sqlBefore ""select * from dual where ("
     * @param values [1,2,3,4,5,6,7,8,9,10]
     * @param "sqlAfter ) and id = 5"
     * @return "select * from dual where (id in (1,2,3) or id in (4,5,6) or id in (7,8,9) or id in (10)"
     */
    public String genInClauseSql(String sqlBefore, List<Integer> values,
            String sqlAfter, String identifier) 
    {
        List<List<Integer>> newLists = splitList(values);
        String stmt = sqlBefore;

        /* now generate the in clause for each list */
        int j = 0; /* keep track of list:newLists index */
        for (List<Integer> list : newLists) {
            stmt = stmt + identifier +" in (";
            StringBuilder innerBuilder = new StringBuilder();

            for (int i = 0; i < list.size(); i++) {
                innerBuilder.append("?,");
            }



            String inClause = innerBuilder.deleteCharAt(
                    innerBuilder.length() - 1).toString();

            stmt = stmt + inClause;
            stmt = stmt + ")";


            if (++j < newLists.size()) {
                stmt = stmt + " OR ";
            }

        }

        stmt = stmt + sqlAfter;
        return stmt;
    }

    /**
     * Method to convert your SQL and a list of ID into a safe prepared
     * statements
     * 
     * @throws SQLException
     */
    public PreparedStatement prepareStatements(String sqlBefore,
            ArrayList<Integer> values, String sqlAfter, Connection c, String identifier)
            throws SQLException {

        /* First split our potentially big list into lots of lists */
        String stmt = genInClauseSql(sqlBefore, values, sqlAfter, identifier);
        PreparedStatement ps = c.prepareStatement(stmt);

        int i = 1;
        for (int val : values)
        {

            ps.setInt(i++, val);

        }
        return ps;

    }

}

2

বসন্ত java.util.Lists কে নাম্বারপ্রেমিটারজেডিবিসিটিম্প্লেটে যেতে দেয় , যা (?,?,?, ...,?) প্রজন্মকে স্বয়ংক্রিয়ভাবে যুক্তির সংখ্যার জন্য উপযুক্ত করে তোলে ?

ওরাকল-এর জন্য, এই ব্লগ পোস্টিংটি oracle.sql.ARRAY (কানেকশন। ক্রিয়েটআরআরইফ ওরাকলের সাথে কাজ করে না) ব্যবহারের বিষয়ে আলোচনা করে। এর জন্য আপনাকে আপনার এসকিউএল স্টেটমেন্টটি সংশোধন করতে হবে:

SELECT my_column FROM my_table where search_column IN (select COLUMN_VALUE from table(?))

ওরাকল টেবিল ফাংশন মান ব্যবহারযোগ্য মত একটি টেবিল মধ্যে পাস অ্যারে রূপান্তরিত INবিবৃতি।


1

instr ফাংশন ব্যবহার করে দেখুন?

select my_column from my_table where  instr(?, ','||search_column||',') > 0

তারপর

ps.setString(1, ",A,B,C,"); 

স্বীকারোক্তিজনকভাবে এটি একটি নোংরা হ্যাকের কিছুটা হলেও এটি স্কয়ার ইঞ্জেকশনের সুযোগগুলি হ্রাস করে না। যাইহোক ওরাকলে কাজ করে।


ওহ, এবং আমি সচেতন যে এটি সূচকগুলি ব্যবহার করবে না
stjohnroe

এটি কিছু স্ট্রিংয়ের জন্য কাজ করবে না, উদাহরণস্বরূপ, যদি স্ট্রিংটিতে একটি ',' থাকে।
ডেভিড পোর্টাবেলা

1

সোমোলুলা আপনাকে এসএসকিউএল IN অপারেটরটিকে প্যারামিটার হিসাবে java.util. সংগ্রহ সংগ্রহ অবজেক্ট সরবরাহ করার মাধ্যমে সমর্থন করে। এটি একটি সঙ্গে একটি প্রস্তুত বিবৃতি তৈরি? উপাদান প্রতিটি সংগ্রহের জন্য। উদাহরণ 4 দেখুন (উদাহরণস্বরূপ এসকিউএল হ'ল কী তৈরি হয়েছে তা স্পষ্ট করার জন্য একটি মন্তব্য কিন্তু সোমোলুলা ব্যবহার করেন না)।


1

পরিবর্তে ব্যবহার

SELECT my_column FROM my_table where search_column IN (?)

হিসাবে SQL বিবৃতি ব্যবহার করুন

select id, name from users where id in (?, ?, ?)

এবং

preparedStatement.setString( 1, 'A');
preparedStatement.setString( 2,'B');
preparedStatement.setString( 3, 'C');

বা কোনও সঞ্চিত পদ্ধতি ব্যবহার করুন এটি সর্বোত্তম সমাধান হতে পারে, যেহেতু এসকিএল স্টেটমেন্টগুলি ডেটাবেস সার্ভারে সংকলন করা হবে এবং সংরক্ষণ করা হবে


1

আমি প্রস্তুত বিবৃতি সম্পর্কিত অনেকগুলি সীমাবদ্ধতা পেরিয়েছি:

  1. প্রস্তুত বিবৃতি কেবল একই সেশনের (পোস্টগ্রিস) ভিতরে ক্যাশ করা হয়, সুতরাং এটি কেবল সংযোগ পুলিংয়ের সাথেই কাজ করবে
  2. @ বালাসক দ্বারা প্রস্তাবিত প্রচুর ভিন্ন ভিন্ন বিবৃতি ক্যাশেকে অতিরিক্ত ভরাট করতে পারে এবং পূর্বে ক্যাশে থাকা বিবৃতিগুলি বাদ দেওয়া হবে
  3. ক্যোয়ারীটি অনুকূলিত করতে হবে এবং সূচকগুলি ব্যবহার করতে হবে। আপাতদৃষ্টিতে মনে হচ্ছে, যেমন শীর্ষস্থানীয় উত্তরগুলির মধ্যে @ বরিস দ্বারা প্রস্তাবিত যে কোনও (অ্যারে ...) বিবৃতি সূচকগুলি ব্যবহার করতে পারে না এবং ক্যাশে সত্ত্বেও ক্যোয়ারী ধীর হবে
  4. প্রস্তুত বিবৃতিটি ক্যোয়ারী প্ল্যানটিকেও ক্যাশে করে এবং বিবৃতিতে নির্দিষ্ট কোনও পরামিতিগুলির আসল মান অনুপলব্ধ ailable

প্রস্তাবিত সমাধানগুলির মধ্যে আমি এমন একটিটি বেছে নেব যা ক্যোরির পারফরম্যান্স হ্রাস না করে এবং কম প্রশ্নের সংখ্যা তৈরি করে। এটি @ ডন লিঙ্ক থেকে # 4 (কয়েকটি প্রশ্নের ব্যাচিং করা) হবে বা অবিশ্রুত 'জন্য ন্যূনুয়াল মান নির্দিষ্টকরণ করবে?' @ ভ্লাদিমির ডিউজেভ প্রস্তাবিত চিহ্নগুলি


1

আমি স্রেফ এর জন্য একটি পোস্টগ্রিজ এসকিউএল-নির্দিষ্ট বিকল্প তৈরি করেছি। এটি কিছুটা হ্যাক, এবং এটি নিজস্ব উপকারিতা এবং মতামত এবং সীমাবদ্ধতার সাথে আসে তবে এটি কাজ করে বলে মনে হয় এবং এটি কোনও নির্দিষ্ট বিকাশ ভাষা, প্ল্যাটফর্ম বা পিজি ড্রাইভারের মধ্যে সীমাবদ্ধ নয়।

অবশ্যই কৌশলটি হ'ল একক পরামিতি হিসাবে মানগুলির একটি স্বেচ্ছাসেবী দৈর্ঘ্যের সংগ্রহটি পাস করার একটি উপায় খুঁজে বের করা এবং ডিবিকে একাধিক মান হিসাবে স্বীকৃতি দেওয়া। আমার যে সমাধানটি কাজ করছে তা হ'ল সংগ্রহের মানগুলি থেকে একটি সীমিত স্ট্রিং তৈরি করা, সেই স্ট্রিংটিকে একক প্যারামিটার হিসাবে পাস করা এবং স্ট্রিং_টো_আর্রে () পোস্টগ্রিএসকিউএলএর সঠিকভাবে ব্যবহারের জন্য প্রয়োজনীয় কাস্টিংয়ের সাথে ব্যবহার করা।

সুতরাং আপনি যদি "foo", "blah" এবং "abc" অনুসন্ধান করতে চান, আপনি তাদের একত্রে একক স্ট্রিংয়ের সাথে যুক্ত করতে পারেন: 'foo, bla, abc'। এখানে সরাসরি এসকিউএল:

select column from table
where search_column = any (string_to_array('foo,blah,abc', ',')::text[]);

আপনার ফলস্বরূপ মান অ্যারে - ইন্টি, পাঠ্য, ইউউইড ইত্যাদি যা আপনি চান তা আপনি স্পষ্টতই নিক্ষেপ বদলে ফেলবেন এবং কারণ ফাংশনটি একটি একক স্ট্রিংয়ের মান নিচ্ছে (বা দুটি আমি মনে করি আপনি যদি ডিলিমিটারটি কাস্টমাইজ করতে চান তবে পাশাপাশি), আপনি একটি প্রস্তুত বিবৃতিতে এটি প্যারামিটার হিসাবে পাস করতে পারেন:

select column from table
where search_column = any (string_to_array($1, ',')::text[]);

লাইক তুলনা যেমন জিনিস সমর্থন করতে এটি যথেষ্ট নমনীয়:

select column from table
where search_column like any (string_to_array('foo%,blah%,abc%', ',')::text[]);

আবার কোনও প্রশ্ন নেই এটি হ্যাক, তবে এটি কাজ করে এবং আপনার সাথে থাকা সুরক্ষা এবং (সম্ভবত) পারফরম্যান্স সুবিধা সহ * আহেম * বিচ্ছিন্ন পরামিতি গ্রহণকারী প্রাক-সংকলিত প্রস্তুত বিবৃতি ব্যবহার করার অনুমতি দেয় । এটি কি পরামর্শদাতা এবং আসলে পারফরম্যান্ট? স্বাভাবিকভাবেই, এটি নির্ভর করে যেহেতু আপনার স্ট্রিংকে পার্সিং এবং সম্ভবত আপনার ক্যোয়ারী চালানোর আগে সম্ভবত কাস্টিং চলছে। আপনি যদি তিন, পাঁচ, কয়েক ডজন মান প্রেরণের প্রত্যাশা করেন তবে অবশ্যই তা ঠিক আছে। কয়েক হাজার? হ্যাঁ, সম্ভবত এত কিছু না। ওয়াইএমএমভি, সীমাবদ্ধতা এবং ব্যতিক্রমগুলি প্রযোজ্য, কোনও ওয়ারেন্টি এক্সপ্রেস বা অন্তর্নিহিত।

কিন্তু এটি কাজ করে.


0

শুধু সম্পূর্ণতার জন্য: তাই যতদিন মূল্যবোধের সেট অত্যন্ত বড় না হয়, আপনি পারে এছাড়াও কেবল মত একটি বিবৃতি স্ট্রিং-গঠন করা

... WHERE tab.col = ? OR tab.col = ? OR tab.col = ?

এরপরে আপনি () প্রস্তুত করতে পাস করতে পারেন এবং তারপরে সমস্ত মান সেট করতে একটি লুপে সেট এক্সএক্সএক্সএক্স () ব্যবহার করুন। এটি ইয়াকী দেখায়, তবে অনেকগুলি "বড়" বাণিজ্যিক ব্যবস্থা নিয়মিতভাবে এই ধরণের কাজ করে যতক্ষণ না তারা ডিবি-নির্দিষ্ট সীমাতে আঘাত করে, যেমন 32 ওরালিতে বিবৃতি দেওয়ার জন্য (আমার মনে হয় এটি)।

অবশ্যই আপনাকে নিশ্চিত করতে হবে যে সেটটি কখনই অযৌক্তিকভাবে বড় হবে না, বা ঘটনার ক্ষেত্রে ট্র্যাপিংয়ের সময় ত্রুটি করবে।


হ্যাঁ আপনি ঠিক. এই ক্ষেত্রে আমার লক্ষ্যটি ছিল প্রতিবার বিভিন্ন সংখ্যক আইটেম সহ প্রিপেইড স্টেটমেন্টটি পুনরায় ব্যবহার করা।
ক্রিস মাজ্জোলা 16 ই

3
"ওআর" ব্যবহারের উদ্দেশ্যটি অবিচ্ছিন্ন হবে। "IN" এর সাথে লেগে থাকুন এটি পড়া সহজ এবং উদ্দেশ্যটি আরও স্পষ্ট। স্যুইচ করার একমাত্র কারণ যদি ক্যোয়ারির পরিকল্পনাগুলি আলাদা ছিল।
জেমস শেক

0

আদমের ধারণা অনুসরণ করা আপনার প্রস্তুত বিবৃতিটি মাই_ টেবিল থেকে আমার_কলামটি নির্বাচন করুন যেখানে অনুসন্ধান # কলামটি (#) একটি স্ট্রিং এক্স তৈরি করুন এবং এটি "?,?," একটি নম্বর দিয়ে পূরণ করুন? আপনার মানগুলির তালিকার উপর নির্ভর করে তারপরে আপনার নতুন স্ট্রিং এক্স একটি জনবসতির জন্য ক্যোয়ারীতে কেবল # পরিবর্তন করুন


0

আপনার তালিকার আইটেমের সংখ্যার সাথে মিলে একটি নম্বর পেতে প্রিপারেড স্টেটমেন্টে ক্যোরি স্ট্রিং তৈরি করুন। এখানে একটি উদাহরণ:

public void myQuery(List<String> items, int other) {
  ...
  String q4in = generateQsForIn(items.size());
  String sql = "select * from stuff where foo in ( " + q4in + " ) and bar = ?";
  PreparedStatement ps = connection.prepareStatement(sql);
  int i = 1;
  for (String item : items) {
    ps.setString(i++, item);
  }
  ps.setInt(i++, other);
  ResultSet rs = ps.executeQuery();
  ...
}

private String generateQsForIn(int numQs) {
    String items = "";
    for (int i = 0; i < numQs; i++) {
        if (i != 0) items += ", ";
        items += "?";
    }
    return items;
}

5
স্ট্রিংবিল্ডার আর ব্যবহার করার দরকার নেই। সংকলক স্ট্রিংবিল্ডার.অপেন্ডে () তে যাইহোক + চিহ্নগুলি রূপান্তর করে, তাই কোনও পারফরম্যান্স হিট নেই। নিজেকে চেষ্টা করুন :)
neu242

5
@ নিউউ 242: ওহ হ্যাঁ, সংকলকটি ব্যবহার করে StringBuilder। তবে আপনি যেভাবে ভাবেন সেভাবে নয়। ডিসকোপলিং generateQsForInআপনি দেখতে পাচ্ছেন যে প্রতি লুপ পুনরাবৃত্তিকে দুটি নতুন StringBuilderবরাদ্দ করা হয় এবং toStringপ্রতিটিটিতে ডাকা হয়। StringBuilderঅপ্টিমাইজেশান ভালো জিনিস ক্যাচ "x" + i+ "y" + jকিন্তু এক অভিব্যক্তি অতিক্রম প্রসারিত করে না।
এএইচ

@ neu242 আপনি ps.setObject(1,items)তালিকার পুনরাবৃত্তি এবং তারপরে সেটিংটি পরিবর্তে ব্যবহার করতে পারবেন না paramteres?
নেহা চৌধুরী চৌধুরী

0

প্রিপার স্টেস্টমেন্টে আইএন ক্লজের জন্য আমরা বিভিন্ন বিকল্প পন্থা ব্যবহার করতে পারি।

  1. একক জিজ্ঞাসা ব্যবহার করা - ধীরতম পারফরম্যান্স এবং সংস্থান নিবিড়
  2. সঞ্চিতপ্রসেসর ব্যবহার - দ্রুততম তবে ডাটাবেস নির্দিষ্ট
  3. প্রিপার্ডস্টেটমেন্টের জন্য ডায়নামিক ক্যোয়ারী তৈরি করা - ভাল পারফরম্যান্স কিন্তু ক্যাশিংয়ের সুবিধা পান না এবং প্রতিবারের মতো প্রস্তুতিমূলক স্টেটমেন্ট পুনরায় সংযুক্ত করা হয়।
  4. প্রিপেইডস্টেটমেন্ট ক্যোয়ারিতে নুল ব্যবহার করুন - আপনি যখন আইএন আর্গুমেন্টের সীমা জানেন তখন অনুকূল পারফরম্যান্স দুর্দান্ত কাজ করে। যদি কোনও সীমা না থাকে, তবে আপনি ব্যাচে কোয়েরিগুলি কার্যকর করতে পারেন। নমুনা কোড স্নিপেট হয়;

        int i = 1;
        for(; i <=ids.length; i++){
            ps.setInt(i, ids[i-1]);
        }
    
        //set null for remaining ones
        for(; i<=PARAM_SIZE;i++){
            ps.setNull(i, java.sql.Types.INTEGER);
        }

এই বিকল্প দৃষ্টিভঙ্গী সম্পর্কে আরো বিস্তারিত পরীক্ষা করতে পারবেন এখানে


"প্রিপার্ডস্টেটমেন্টের জন্য ডায়নামিক ক্যোয়ারী তৈরি করা - ভাল পারফরম্যান্স কিন্তু ক্যাশিংয়ের সুবিধা পান না এবং প্রতিবারের মতো প্রস্তুতিমূলক স্টেটমেন্ট পুনরায় সংকলিত হয়।" পুনরায় সংস্থানগুলি ক্যাচ করা এবং এড়ানো এটাই যা প্রস্তুত বিবৃতিটি ভাল সম্পাদন করে। অতএব, আমি আপনার দাবির সাথে একমত নই এটি তবে এসকিউএল ইঞ্জেকশনটিকে রোধ করবে যেহেতু আপনি কমাতে / ডায়নামিক ইনপুটটি কমাতে সীমাবদ্ধ করছেন।
ব্র্যান্ডন

আমি আপনার সাথে একমত, তবে এখানে "ভাল পারফরম্যান্স" এই নির্দিষ্ট দৃশ্যের জন্য। এটি এপ্রোচ 1 এর চেয়ে ভাল পারফরম্যান্স দিচ্ছে, তবে পদ্ধতির 2 দ্রুততম is
পঙ্কজ

0

কিছু পরিস্থিতিতে regexp সাহায্য করতে পারে। আমি ওরাকলে যাচাই করেছিলাম এমন একটি উদাহরণ এখানে রয়েছে এবং এটি কার্যকর হয়।

select * from my_table where REGEXP_LIKE (search_column, 'value1|value2')

তবে এটির সাথে বেশ কয়েকটি ত্রুটি রয়েছে:

  1. এটি প্রয়োগ করা যে কোনও কলামটি অন্তত অন্তর্নিহিতভাবে বর্ণচর / চরে রূপান্তর করা উচিত।
  2. বিশেষ চরিত্রগুলির সাথে সাবধান হওয়া দরকার।
  3. এটি পারফরম্যান্সকে ধীর করতে পারে - আমার ক্ষেত্রে আইএন সংস্করণ সূচি এবং রেঞ্জ স্ক্যান ব্যবহার করে এবং আরজিইএক্সপি সংস্করণ পূর্ণ স্ক্যান করে।

0

বিভিন্ন ফোরামে বিভিন্ন সমাধানগুলি পরীক্ষা করার পরে এবং কোনও ভাল সমাধান না পাওয়ার পরে, আমি নীচের হ্যাকটি অনুভব করি যা আমি নিয়ে এসেছি, এটি অনুসরণ করা এবং কোড করা সবচেয়ে সহজ:

উদাহরণ: ধরুন আপনার কাছে 'IN' ধারাটিতে পাস করার জন্য একাধিক পরামিতি রয়েছে। 'IN' ধারাটির ভিতরে কেবল একটি ডামি স্ট্রিং রাখুন, বলুন, "PARAM" এই ডামি স্ট্রিংয়ের জায়গায় যে পরামিতিগুলি আসবে তার তালিকা বোঝায় না।

    select * from TABLE_A where ATTR IN (PARAM);

আপনি আপনার জাভা কোডের একক স্ট্রিং ভেরিয়েবলের মধ্যে সমস্ত পরামিতি সংগ্রহ করতে পারেন। ইহা এভাবে করা যাবে:

    String param1 = "X";
    String param2 = "Y";
    String param1 = param1.append(",").append(param2);

আপনি আমাদের ক্ষেত্রে কমা দ্বারা আলাদা করা আপনার সমস্ত পরামিতি যুক্ত করতে পারেন, আমাদের ক্ষেত্রে 'প্যারাম 1', একটি একক স্ট্রিং ভেরিয়েবল।

একক স্ট্রিংয়ে সমস্ত পরামিতি সংগ্রহ করার পরে আপনি কেবল নিজের ক্যোয়ারির ডামি পাঠ্যটি প্রতিস্থাপন করতে পারবেন, এই ক্ষেত্রে "PARAM", স্ট্রিং প্যারামিটার, অর্থাৎ প্যারাম 1 দিয়ে। এখানেই সব পাবেন আপনি যা করতে চান:

    String query = query.replaceFirst("PARAM",param1); where we have the value of query as 

    query = "select * from TABLE_A where ATTR IN (PARAM)";

আপনি এখন এক্সিকিউটিকিউরি () পদ্ধতিটি ব্যবহার করে আপনার ক্যোয়ারীটি কার্যকর করতে পারেন। কেবল নিশ্চিত হয়ে নিন যে আপনার কোয়েরীতে কোথাও "PARAM" শব্দটি নেই। কোয়েরিতে এই জাতীয় শব্দ আসার কোনও সম্ভাবনা নেই তা নিশ্চিত করতে আপনি "PARAM" শব্দের পরিবর্তে বিশেষ অক্ষর এবং বর্ণমালার সংমিশ্রণটি ব্যবহার করতে পারেন। আশা করি সমাধান পেয়ে গেছেন।

দ্রষ্টব্য: যদিও এটি প্রস্তুত কোয়েরি না হলেও এটি আমার কোডটি করতে চেয়েছিল এমন কাজ করে।


0

কেবল সম্পূর্ণতার জন্য এবং কারণ আমি অন্য কাউকে এটি প্রস্তাব দিতে দেখিনি:

উপরের জটিল পরামর্শগুলির বাস্তবায়ন করার আগে বিবেচনা করুন এসকিউএল ইঞ্জেকশনটি আপনার দৃশ্যে আসলেই কোনও সমস্যা কিনা।

অনেক ক্ষেত্রে আইএন (...) সরবরাহিত মান হ'ল আইডির একটি তালিকা যা এমনভাবে উত্পন্ন হয়েছে যে আপনি নিশ্চিত হতে পারেন যে কোনও ইনজেকশন সম্ভব নয় ... (যেমন পূর্ববর্তী নির্বাচনের ফলাফলগুলি কিছু_ টেবিল থেকে কিছু_আইড যেখানে some_condition।)

যদি এমনটি হয় তবে আপনি এই মানটিটি সজাগ করতে পারেন এবং এর জন্য পরিষেবাগুলি বা প্রস্তুত বিবৃতি ব্যবহার না করতে বা এই কোয়েরির অন্যান্য পরামিতিগুলির জন্য এগুলি ব্যবহার না করে।

query="select f1,f2 from t1 where f3=? and f2 in (" + sListOfIds + ");";

0

প্রিপার স্টেটমেন্ট এসকিউএল ইন ক্লজ সহ ডিল করার কোনও ভাল উপায় সরবরাহ করে না। প্রতি http://www.javaranch.com / জার্নাল / 200510 / জার্নাল 2005510.jsp# a2 "আপনি এসকিউএল স্টেটমেন্টের অংশ হওয়ার জন্য বোঝানো জিনিসগুলি প্রতিস্থাপন করতে পারবেন না This এটি প্রয়োজনীয় কারণ যদি এসকিউএল নিজেই পরিবর্তন করতে পারে তবে ড্রাইভার স্টেটমেন্টটি প্রাকপাইল করতে পারে না S আমি নিম্নলিখিত পদ্ধতির ব্যবহার করে শেষ করেছি:

String query = "SELECT my_column FROM my_table where search_column IN ($searchColumns)";
query = query.replace("$searchColumns", "'A', 'B', 'C'");
Statement stmt = connection.createStatement();
boolean hasResults = stmt.execute(query);
do {
    if (hasResults)
        return stmt.getResultSet();

    hasResults = stmt.getMoreResults();

} while (hasResults || stmt.getUpdateCount() != -1);

0

সেটআরেই সেরা সমাধান তবে এটি বহু পুরানো ড্রাইভারের জন্য উপলভ্য নয়। নিম্নলিখিত workaround java8 ব্যবহার করা যেতে পারে

String baseQuery ="SELECT my_column FROM my_table where search_column IN (%s)"

String markersString = inputArray.stream().map(e -> "?").collect(joining(","));
String sqlQuery = String.format(baseSQL, markersString);

//Now create Prepared Statement and use loop to Set entries
int index=1;

for (String input : inputArray) {
     preparedStatement.setString(index++, input);
}

এই সমাধানটি অন্যান্য কুরুচিপূর্ণের চেয়ে ভাল যেখানে লুপের সমাধানগুলি যেখানে ম্যানুয়াল পুনরাবৃত্তির দ্বারা ক্যোরি স্ট্রিং তৈরি করা হয়


0

এটি আমার জন্য কাজ করেছে (স্যুইচোকোড):

public class SqlHelper
{
    public static final ArrayList<String>platformList = new ArrayList<>(Arrays.asList("iOS","Android","Windows","Mac"));

    public static final String testQuery = "select * from devices where platform_nm in (:PLATFORM_NAME)";
}

বাইন্ডিং নির্দিষ্ট করুন:

public class Test extends NamedParameterJdbcDaoSupport
public List<SampleModelClass> runQuery()
{
    //define rowMapper to insert in object of SampleClass
    final Map<String,Object> map = new HashMap<>();
    map.put("PLATFORM_LIST",DeviceDataSyncQueryConstants.platformList);
    return getNamedParameterJdbcTemplate().query(SqlHelper.testQuery, map, rowMapper)
}

0

এসকিউএলাইট এবং ওরাকল ডাটাবেসের জন্য আমার উদাহরণ।

লুপের জন্য প্রথমটি হ'ল প্রস্তুতিমূলক অবজেক্ট তৈরি করা।

লুপের জন্য দ্বিতীয়টি হ'ল প্রস্তুতিমূলক পরামিতিগুলির মান সরবরাহ করার জন্য Values

List<String> roles = Arrays.asList("role1","role2","role3");
Map<String, String> menu = getMenu(conn, roles);

public static Map<String, String> getMenu(Connection conn, List<String> roles ) {
    Map<String, String> menu = new LinkedHashMap<String, String>();

    PreparedStatement stmt;
    ResultSet rset;
    String sql;
    try {
        if (roles == null) {
            throw new Exception();
        }
        int size = roles.size();
        if (size == 0) {
            throw new Exception("empty list");
        }
        StringBuilder sb = new StringBuilder();
        sb.append("select page_controller, page_name from pages "
                + " where page_controller in (");
        for (int i = 0; i < size; i++) {
            sb.append("?,");
        }
        sb.setLength(sb.length() - 1);
        sb.append(") order by page_id");
        sql = sb.toString();
        stmt = conn.prepareStatement(sql);
        for (int i = 0; i < size; i++) {
            stmt.setString(i + 1, roles.get(i));
        }
        rset = stmt.executeQuery();
        while (rset.next()) {
            menu.put(rset.getString(1), rset.getString(2));
        }

        conn.close();
    } catch (Exception ex) {
        logger.info(ex.toString());
        try {
            conn.close();
        } catch (SQLException e) {
        }
        return menu;
    }
    return menu;
}

-3

আমার কার্যতালিকা (জাভাস্ক্রিপ্ট)

    var s1 = " SELECT "

 + "FROM   table t "

 + "  where t.field in ";

  var s3 = '(';

  for(var i =0;i<searchTerms.length;i++)
  {
    if(i+1 == searchTerms.length)
    {
     s3  = s3+'?)';
    }
    else
    {
        s3  = s3+'?, ' ;
    }
   }
    var query = s1+s3;

    var pstmt = connection.prepareStatement(query);

     for(var i =0;i<searchTerms.length;i++)
    {
        pstmt.setString(i+1, searchTerms[i]);
    }

SearchTerms আপনার ইনপুট / কী / ফিল্ডস ইত্যাদি সমন্বিত অ্যারেটি

আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.