webForumDet fria alternativet

PDO, MSSQL och IN()

PHP

9 svar · 586 visningar · startad av Troxy

Medlem sedan mars 20041 505 inlägg
Frågan#1

Hejsan, PDO är ju ganska nytt i PHP så vi får se om någon här kan hjälpa mig.
Nåväl, säg att jag har en array som innehåller id-nummer:

$id = array(10,11,12,13);

Nu vill jag ställa en SQL-fråga mot SQL Server 2000 som hämtar alla användare med dessa id-nummer.
En SQL-fråga för detta borde se ut såhär:

SELECT username, password FROM users WHERE id IN(10,11,12,13)

Men frågan är, hur bygger jag denna SQL-fråga med PDO? :q

Det verkar bara gå att använda parameters och placeholders för direkta värden (vilket iof är logiskt).
Så tyvärr så fungerar inte detta:

$stmt = $dbHost->prepare("SELECT username, password FROM users WHERE id IN(?)");
$stmt->bindValue(1, implode(',', $id));
$stmt->execute();

Jag hoppas nu att någon här har hunnit bekanta sig med PDO och kan hjälpa mig :)

Medlem sedan juni 20001 251 inlägg
#2
$stmt = $dbHost->prepare("SELECT username, password FROM users WHERE id IN(?)");
$id = array_map('intval', $id);
$ids = implode(', ', $id);

if ($stmt->execute(array($ids))) {
  while ($row = $stmt->fetch()) {
    print_r($row);
  }
}
Medlem sedan dec. 19996 721 inlägg
#3

Stoppa in lika många frågetecken som du har element i arrayen och execute:a med arrayen som parameter. Imploda inte.

Medlem sedan juni 20001 251 inlägg
#4

Kanske ska du också lägga in någon form av felhantering:

try {
//$dbHost = "";
$stmt = $dbHost->prepare("SELECT username, password FROM users WHERE id IN(?)"); 
$id = array_map('intval', $id); 
$ids = implode(', ', $id); 

if ($stmt->execute(array($ids))) { 
  while ($row = $stmt->fetch()) { 
    print_r($row); 
  } 
} 
} catch (PDOException $e) {
   print "Error!: " . $e->getMessage() . "<br/>";
   die();
}
Medlem sedan mars 20041 505 inlägg
#5

stevenet skrev:

$id = array_map('intval', $id);

Hm, om min array redan innehåller integers, vad gör detta för skillnad?
Hur som helst så verkar ditt exempel tyvärr inte fungera... :(

emission skrev:

Stoppa in lika många frågetecken som du har element i arrayen och execute:a med arrayen som parameter. Imploda inte.

Tyvärr, antalet element kan variera...och då måste jag bygga upp antalet frågetecken dynamiskt genom att konkatinera strängar. Blir inte smidigt. :l

Alltså själva grundproblemet är ju att PDO behandlar alla nummer som ett vanligt värde (en sträng i detta fall), vilket betyder att den färdiga SQL-frågan ser ut såhär:

SELECT username, password FROM users WHERE id IN('10,11,12,13')

Denna fråga returnerar såklart 0 poster eftersom den inte matchar någonting.
Jag skulle ju kanske kunna strunta i prepare() och hårdkoda mina nummer i SQL-frågan med hjälp av exempelvis sprintf() men då måste jag klura ut hur jag säkrar den från SQL-injectioner på ett bra sätt. :l

Medlem sedan juni 20001 251 inlägg
#6

Ouups, fel av mig. Returnerade visst bara första nuffran. :r

Intval är till för att säkra att indatan är heltal av följande skäl:

"You cannot bind multiple values to a single parameter; for example, you cannot bind two values to a single named parameter in an IN() clause"

$id = array(10,11,12,13);
try { 
$dbHost = "";

$id = array_map('intval', $id); 
$ids = implode(', ', $id); 
$sql = 'SELECT username, password FROM users WHERE id IN(%s)';
$sql = sprintf($sql, $ids);

$stmt = $dbHost->prepare($sql);

if ($stmt->execute()) { 
  while ($row = $stmt->fetch()) { 
    print_r($row); 
  } 
} 
else {
	echo "0 rows returned. SQL: ".$sql;
}
} catch (PDOException $e) { 
   print "Error!: " . $e->getMessage() . "<br/>"; 
   die(); 
}
Medlem sedan mars 20041 505 inlägg
#7

stevenet, hur ser din PDO-koppling ut? Min ser ut såhär:

$dbHost = new PDO('mssql:host=127.0.0.1;dbname=Labb', 'blabla', 'blabla');

Nu märkte jag att felrapporteringen var avslagen. x(
Nu får jag den här varningen med ditt kodexempel:

PHP Warning: PDOStatement::execute(): SQLSTATE[HY000]: General error: 10007 Syntax error converting the varchar value '136483, 138565, 130255, 109421' to a column of data type int.

Medlem sedan juni 20001 251 inlägg
#8

Nu kör jag mysql, men det ser korrekt ut.

Det felet var väl väntat, nu bör det dock fungera.

Medlem sedan mars 20041 505 inlägg
#9

Tack stevenet! Din senaste lösning fungerar och är smidig. :)

Men nog tycker jag allt att PDO borde utrustas med en ny typ av placeholder som kan användas för att bygga upp själva SQL-frågan också.
Samma problem uppstår ju om man exempelvis vill använda placeholders för tabell-namn.

Medlem sedan dec. 19996 721 inlägg
#10

Troxy skrev:

Tack stevenet! Din senaste lösning fungerar och är smidig. :)

Men nog tycker jag allt att PDO borde utrustas med en ny typ av placeholder som kan användas för att bygga upp själva SQL-frågan också.
Samma problem uppstår ju om man exempelvis vill använda placeholders för tabell-namn.

Jo, men det är inte upp till PDO. Placeholder är ett missvisande namn. Egentligen är det SQL-parametrar och dessa kan inte användas för arrayer (därav mitt tidigare svar) och inte heller för tabellnamn (eftersom det skulle förstöra hela idén med prepared statements).

Om det är placeholders du vill åt så är det sprintf som hanterar det. Vill du njuta av säkerheten och prestandan som parametriserade frågor ger så får du ocksp leva med begränsningarna.

En lagom lång lista med frågetecken kan du göra med

ltrim(str_repeat(",?",count($id)),",")
264 ms totalt · 4 externa anrop · v20260731065814-full.a51de22e
122 ms — deklarationer (db)
0 ms — hämta statistik (cache)
139 ms — hämta tråd, inlägg och bilagor (db)
117 ms — ändringar (db)