webForumDet fria alternativet

PDO, MSSQL och IN()

9 svar · 586 visningar · startad av Troxy

TroxyMedlem sedan mars 20041 505 inlägg
#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 :)

stevenetMedlem 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);
  }
}
emissionMedlem 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.

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

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

stevenetMedlem 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.

TroxyMedlem 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.

emissionMedlem 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)),",")
129 ms totalt · 3 externa anrop · v20260731065814-full.30151723
0 ms — hämta forumlista (cache)
0 ms — hämta statistik (cache)
127 ms — hämta tråd, inlägg och bilagor (db)