2008-11-29 18 views
21

आप PHP में एक तैयार MySQL कथन कैसे लिखेंगे जो प्रत्येक बार भिन्न संख्या में तर्क लेता है? एक उदाहरण इस तरह के प्रश्न है:MySQL एक परिवर्तनीय आकार चर सूची के साथ तैयार बयान

SELECT `age`, `name` FROM `people` WHERE id IN (12, 45, 65, 33) 

IN खंड id रों हर बार चलाया जाता है की एक अलग नंबर होगा।

मेरे दिमाग में मेरे दो संभावित समाधान हैं लेकिन यह देखना चाहते हैं कि कोई बेहतर तरीका है या नहीं।

संभावित समाधान 1 कथन को 100 चर स्वीकार करते हैं और बाकी को डमी मानों के साथ भरें, तालिका में नहीं होने की गारंटी है; 100 से अधिक मूल्यों के लिए एकाधिक कॉल करें।

संभावित समाधान 2 तैयार कथन का उपयोग न करें; संभावित इंजेक्शन हमलों के लिए कड़ाई से क्वेरी जांच बनाना और चलाएं।

+0

मुझे लगता है मैं दोनों से बच जाएंगे आपके द्वारा सुझाए गए समाधान। तैयार बयान आपके कोड को अधिक आसान बनाते हैं। और यदि आपका कोई छोटा सूची है तो आपका पहला समाधान बहुत अक्षम और अपर्याप्त लगता है। – Zoredache

+0

मैं सहमत हूं कि मैंने सोचा था कि ये विचार दूसरों के दिमाग को उत्तेजित कर सकते हैं। :) – smarthall

+0

SQL सर्वर के लिए, देखें [खंड में एक SQL को पैरामीटरेट करना?] (Http://stackoverflow.com/q/337704/90527) – outis

उत्तर

21

मैं कुछ समाधानों के बारे में सोच सकता हूं।

एक समाधान एक अस्थायी तालिका बनाने के लिए हो सकता है। खंड में आपके प्रत्येक पैरामीटर के लिए तालिका में डालें। फिर अपनी अस्थायी तालिका के खिलाफ एक सरल जुड़ाव करें।

ऐसा कुछ और करने का तरीका हो सकता है।

$dbh=new PDO($dbConnect, $dbUser, $dbPass); 
$parms=array(12, 45, 65, 33); 
$parmcount=count($parms); // = 4 
$inclause=implode(',',array_fill(0,$parmcount,'?')); // = ?,?,?,? 
$sql='SELECT age, name FROM people WHERE id IN (%s)'; 
$preparesql=sprintf($sql,$inclause); // = example statement used in the question 
$st=$dbh->prepare($preparesql); 
$st->execute($parms); 

मुझे लगता है, लेकिन कोई सबूत है, कि पहले समाधान बड़ा सूचियों के लिए बेहतर हो सकता है, और बाद में छोटे सूचियों के लिए काम करेगा।


@orrd खुश करने के लिए यहां एक terse संस्करण है।

$dbh=new PDO($dbConnect, $dbUser, $dbPass); 
$parms=array(12, 45, 65, 33); 
$st=$dbh->prepare(sprintf('SELECT age, name FROM people WHERE id IN (%s)', 
          implode(',',array_fill(0,count($parms),'?')))); 
$st->execute($parms); 

+1

मुझे आपका दूसरा सुझाव पसंद है। ऐसा करें और प्रदर्शन को एक मुद्दा होने तक इसके बारे में भूल जाओ। उस बिंदु पर यह पहले विकल्प की जांच के लायक हो सकता है। – benlumley

+0

अगर केवल मैंने सोचा होगा! आपका पहला समाधान उस सटीक चीज़ की तरह लगता है जिसे मैं ढूंढ रहा था। – smarthall

+0

मैंने पैटर्न # 2 अक्सर उपयोग किया है। पर्ल के डीबीआई में ready_cached() फ़ंक्शन है, इसलिए यदि आप समान संख्या में प्लेसहोल्डर्स से पूछताछ करते हैं, तो यह स्टेटमेंट हैंडल का पुन: उपयोग करेगा। हालांकि PHP के बारे में निश्चित नहीं है .. –

2

कृपया तालिका बंद # 2 ले। तैयार बयान एकमात्र तरीका है जिसे आपको एसक्यूएल इंजेक्शन के खिलाफ स्वयं को बचाने पर विचार करना चाहिए।

हालांकि, आप बाध्यकारी चर के गतिशील सेट उत्पन्न कर सकते हैं। यानी अगर आपको 7 (या 103) की आवश्यकता है तो 100 मत बनाओ।

+0

क्या? यह समझ में नहीं आता है। वह तैयार बयान का उपयोग कर रहा है, लेकिन वह गतिशील रूप से प्लेसहोल्डर्स की संख्या निर्धारित कर रहा है। –

+0

परिदृश्य # 1 में, वह # 2 में, 100 पैरामीटर लेने के लिए क्वेरी को स्थैतिक रूप से परिभाषित कर रहा था, वह एक तैयार कथन का उपयोग नहीं कर रहा था। मेरा सुझाव था कि बाध्यकारी के साथ क्वेरी को गतिशील रूप से बनाना, जो वही बात है जो आप कह रहे हैं। – Dustin

+0

वूप्स। मैं http://stackoverflow.com/questions/327274/mysql-prepared-statements-with-a-variable-size-variable-list#327384 से # 2 पढ़ रहा था। माफ़ कीजिये! –

4

सभ्य एसक्यूएल रैपर सरणी मानों के लिए बाध्यकारी समर्थन करते हैं। अर्थात

$sql = "... WHERE id IN (?)"; 
$values = array(1, 2, 3, 4); 
$result = $dbw -> prepare ($sql, $values) -> execute(); 
+0

मैं वास्तव में MySQL (न तो mysql, mysqli और न ही पीडीओ) के लिए किसी मूल PHP डेटाबेस एक्सेस लाइब्रेरी के बारे में नहीं जानता जो सरणी प्रकार के बाध्यकारी पैरामीटर के लिए अनुमति देता है। –

+0

वापस जब मैं कुछ साल पहले PHP में विकास कर रहा था, adodb मेरे लिए वास्तव में एक अच्छा काम किया था। मुझे लगता है कि आपको इसे देखना चाहिए। – Eimantas

+1

कोई भी ढांचा जो सूची को विस्तारित करके और इसे तैयार करने से पहले SQL क्वेरी में इंटरपोल करके कर रहा है()। यह बाध्य पैरामीटर के समान नहीं है। –

1

आप केवल अपने IN खंड में पूर्णांक मूल्यों उपयोग कर रहे हैं, वहाँ कुछ भी नहीं है कि SQL मानकों के उपयोग के बिना गतिशील रूप से आपकी क्वेरी के निर्माण के खिलाफ तर्क है है।

function convertToInt(&$value, $key) 
{ 
    $value = intval($value); 
} 

$ids = array('12', '45', '65', '33'); 
array_walk($ids, 'convertToInt'); 
$sql = 'SELECT age, name FROM people WHERE id IN (' . implode(', ', $ids) . ')'; 
// $sql will contain SELECT age, name FROM people WHERE id IN (12, 45, 65, 33) 

लेकिन बिना शक समाधान here इस समस्या का अधिक सामान्य दृष्टिकोण है।

+0

"तैयार कथन के साथ एक्स कैसे करें" का समाधान क्यों है? गतिशील रूप से एक अलग क्वेरी बनाने के लिए? यदि आप कैश किए गए क्वेरी प्लान का पुनः उपयोग करने के लिए तैयार कथन का उपयोग कर रहे हैं, तो आपने इसे कमजोर कर दिया है।यदि आप एसक्यूएल इंजेक्शन को रोकने के लिए ऐसा कर रहे हैं, तो यह अलग है। – Brandon

2

मुझे मेरा जवाब मिला: http://bugs.php.net/bug.php?id=43568
यह मेरी समस्या का मेरा समाधान समाधान है। अब मैं गतिशील रूप से जितना चाहूं उतने पैरामीटर का उपयोग कर सकता हूं। वे वही संख्या होंगे जैसे मेरे पास एक सरणी में है या इस मामले में मैं अंतिम क्वेरी से आईडी को पास कर रहा हूं (जो सभी आईडी प्राप्त करता है जहां ईमेल = '[email protected]') सभी को प्राप्त करने के लिए गतिशील क्वेरी में इन आईडी में से प्रत्येक के बारे में जानकारी इससे कोई फर्क नहीं पड़ता कि मुझे कितनी जरूरत है।

<?php $NumofIds = 2; //this is the number of ids i got from the last query 
    $parameters=implode(',',array_fill(0,$NumofIds,'?')); 
    // = ?,? the same number of ?'s as ids we are looking for<br /> 
    $paramtype=implode('',array_fill(0,$NumofIds,'i')); // = ii<br/> 
    //make the array to build the bind_param function<br/> 
    $idAr[] = $paramtype; //'ii' or how ever many ?'s we have<br/> 
    while($statement->fetch()){ //this is my last query i am getting the id out of<br/> 
     $idAr[] = $id; 
    } 

    //now this array looks like this array:<br/> 
    //$idAr = array('ii', 128, 237); 

    $query = "SELECT id,studentid,book_title,date FROM contracts WHERE studentid IN ($parameters)"; 
    $statement = $db->prepare($query); 
    //build the bind_param function 
    call_user_func_array (array($statement, "bind_param"), $idAr); 
    //here is what we used to do before making it dynamic 
    //statement->bind_param($paramtype,$v1,$v2); 
    $statement->execute(); 
?> 
+0

यह समाधान मेरे लिए काम किया। – Justin

10

FIND_IN_SET function जिसका दूसरा पैरामीटर कॉमा सेपरेटेड वैल्यूज़ की एक श्रृंखला है भी है:

SELECT age, name FROM people WHERE FIND_IN_SET(id, '12,45,65,33') 
+3

इसके साथ समस्या यह है: यह प्रत्येक पंक्ति के लिए FIND_IN_SET निष्पादित करते समय पूर्ण तालिका स्कैन करने के लिए डिफ़ॉल्ट अनुक्रमणिका का उपयोग नहीं करेगा। –

0

मैं आज एक समान समस्या थी और मैं इस विषय पाया। उत्तर को देखकर और Google के चारों ओर खोजना मुझे एक सुंदर समाधान मिला।

हालांकि, मेरी समस्या थोड़ा और जटिल है। क्योंकि मैंने बाध्यकारी मान और गतिशील भी तय किया है।

यह समाधान है।

$params = array() 
$all_ids = $this->get_all_ids(); 

for($i = 0; $i <= sizeof($all_ids) - 1; $i++){ 
    array_push($params, $all_ids[$i]['id']); 
} 

$clause = implode(',', array_fill(0, count($params), '?')); // output ?, ?, ? 
$total_i = implode('', array_fill(0, count($params), 'i')); // output iiii 

$types = "ss" . $total_i; // will reproduce : ssiiii ..etc 

// %% it's necessary because of sprintf function 
$query = $db->prepare(sprintf("SELECT * 
           FROM clients  
           WHERE name LIKE CONCAT('%%', ?, '%%') 
           AND IFNULL(description, '') LIKE CONCAT('%%', ?, '%%') 
           AND id IN (%s)", $clause)); 

$thearray = array($name, $description); 
$merge = array_merge($thearray, $params); // output: "John", "Cool guy!", 1, 2, 3, 4 

// We need to pass variables instead of values by reference 
// So we need a function to that 
call_user_func_array('mysqli_stmt_bind_param', array_merge (array($query, $types), $this->makeValuesReferenced($merge))); 

और समारोह makeValuesreferenced: इस 'पता है कि कैसे' प्राप्त करने के लिए

public function makeValuesReferenced($arr){ 
    $refs = array(); 
    foreach($arr as $key => $value) 
     $refs[$key] = &$arr[$key]; 
    return $refs; 
} 

लिंक: https://bugs.php.net/bug.php?id=49946, PHP append one array to another (not array_push or +), [PHP]: Error -> Too few arguments in sprintf();, http://no2.php.net/manual/en/mysqli-stmt.bind-param.php#89171, Pass by reference problem with PHP 5.3.1

संबंधित मुद्दे