Kommentar
Åtkomst till den här sidan kräver auktorisering. Du kan prova att logga in eller ändra kataloger.
Åtkomst till den här sidan kräver auktorisering. Du kan prova att ändra kataloger.
Båda drivrutinerna kan köra en sats en gång eller förbereda en sats för upprepad körning. Använd parametrar för värden som tillhandahålls av användare, även om du bara kör en fråga en gång.
Välj exekverings-API:et separat från hur du hämtar resultat. Att förbereda en fråga hämtar inte dess rader, och buffring av ett resultat gör inte en fråga parameteriserad. Följande exempel förbereder en skrivskyddad fråga en gång, kör den två gånger med olika värden och konsumerar varje resultat innan nästa exekvering.
| Körstil | SQLSRV | PDO_SQLSRV |
|---|---|---|
| Kör en fråga en gång, utan parametrar | sqlsrv_query | PDO::query för resultat, eller PDO::exec för antal påverkade rader |
| Kör en query en gång, med parametrar | sqlsrv_query($conn, $sql, $params) |
PDO::prepare() följt av PDOStatement::execute() |
| Förbered en gång, kör upprepade gånger med olika parametrar | sqlsrv_prepare + sqlsrv_execute | PDO::p repare + PDOStatement::execute |
sqlsrv_query() förbereder och kör i ett anrop och returnerar en statementresurs.
sqlsrv_prepare() returnerar en statement-resurs utan att köra den; anropa sqlsrv_execute() för varje exekvering. PDO query() exekverar och returnerar PDOStatement, medan exec() returnerar en påverkad radräkning istället för ett resultatobjekt. En lyckad exec() kan returnera 0, så jämför ett felreturvärde med === false, inte if (!$count).
PDO::query() accepterar inte en array av parametervärden. Dess valfria argument konfigurerar hämtning. För parameteriserade PDO_SQLSRV frågor, använd prepare() och execute().
Att förbereda en gång låter dig återanvända ett statement handle med olika värden. Detta är ingen garanti för att servern aldrig kompilerar om sin exekveringsplan. PDO_SQLSRV använder förberedd exekvering som standard, inklusive för PDO::query(); dess attributet direct-query styr det separata valet.
Förbered en gång och kör flera gånger
Dessa är separata, kompletta kommandoradsprogram. Installera och ladda det valda tillägget, och ange SQL_SERVER, SQL_DATABASE, SQL_USER och SQL_PASSWORD i PHP-processmiljön. Använd en SQL Server-testdatabas och ett servercertifikat som klienten litar på. För miljöuppsättning och lösenordslösa alternativ, se Anslutning till servern.
Spara det faktiska databasnamnet, användarnamnet och lösenordet i miljövariablerna. Båda programmen specialteckenbehandlar dessa värden en gång genom att dubbla stängande klamrar och innesluta varje värde i klamrar, enligt PHP-drivrutinens ODBC-liknande anslutningshantering. Att escapa anslutningsvärden är separat från att binda frågeparametrar.
Programmen kör en Transact-SQL-fråga (T-SQL) över två inline-rader utan att skapa en tabell eller ändra data. Spara varje program i sin egen .php fil och kör det med php <file>. Ingen exempeldatabas krävs.
SQLSRV
Skicka $productId med referens med &$productId. Varje sqlsrv_execute() använder sedan sitt aktuella värde.
SQLSRV_FETCH_ASSOC returnerar en array med kolumnnamnen som nycklar. Ett null resultat betyder att det inte finns fler rader; false indikerar ett fel.
<?php
$config = [];
foreach (['SQL_SERVER', 'SQL_DATABASE', 'SQL_USER', 'SQL_PASSWORD'] as $name) {
$value = getenv($name);
if ($value === false || $value === '') {
throw new RuntimeException("Set $name before running this example.");
}
$config[$name] = $value;
}
foreach (['SQL_DATABASE', 'SQL_USER', 'SQL_PASSWORD'] as $name) {
$config[$name] = '{' . str_replace('}', '}}', $config[$name]) . '}';
}
$conn = sqlsrv_connect($config['SQL_SERVER'], [
'Database' => $config['SQL_DATABASE'],
'UID' => $config['SQL_USER'],
'PWD' => $config['SQL_PASSWORD'],
'Encrypt' => true,
'TrustServerCertificate' => false,
'LoginTimeout' => 30,
'CharacterSet' => 'UTF-8',
]);
if ($conn === false) {
throw new RuntimeException(print_r(sqlsrv_errors(), true));
}
$sql = "SELECT Name FROM (VALUES (1, 'Bike'), (2, 'Helmet')) AS Product(ProductId, Name) WHERE ProductId = ?";
$productId = 1;
$stmt = null;
try {
$stmt = sqlsrv_prepare($conn, $sql, [&$productId], ['QueryTimeout' => 30]);
if ($stmt === false) {
throw new RuntimeException(print_r(sqlsrv_errors(), true));
}
foreach ([1 => 'Bike', 2 => 'Helmet'] as $productId => $expected) {
if (!sqlsrv_execute($stmt)) {
throw new RuntimeException(print_r(sqlsrv_errors(), true));
}
$names = [];
while (($row = sqlsrv_fetch_array($stmt, SQLSRV_FETCH_ASSOC)) !== null) {
if ($row === false) {
throw new RuntimeException(print_r(sqlsrv_errors(), true));
}
$names[] = $row['Name'];
}
if ($names !== [$expected]) {
throw new RuntimeException('Expected exactly one matching product.');
}
echo $names[0], PHP_EOL;
}
} finally {
if (is_resource($stmt)) {
sqlsrv_free_stmt($stmt);
}
sqlsrv_close($conn);
}
PDO_SQLSRV
Anslut $productId en enda gång med bindParam(). Varje execute() läser av sitt aktuella värde.
PDO::PARAM_INT specificerar parameterns PHP/PDO-bindningstyp; den styr inte kolumnernas PHP-typ som hämtas i resultatet. Ställ in undantagsläge så att ett databasfel avbryter exekveringen istället för att förväxlas med ett tomt resultat.
<?php
$config = [];
foreach (['SQL_SERVER', 'SQL_DATABASE', 'SQL_USER', 'SQL_PASSWORD'] as $name) {
$value = getenv($name);
if ($value === false || $value === '') {
throw new RuntimeException("Set $name before running this example.");
}
$config[$name] = $value;
}
foreach (['SQL_DATABASE', 'SQL_USER', 'SQL_PASSWORD'] as $name) {
$config[$name] = '{' . str_replace('}', '}}', $config[$name]) . '}';
}
$server = $config['SQL_SERVER'];
$database = $config['SQL_DATABASE'];
$pdo = new PDO(
"sqlsrv:Server=$server;Database=$database;Encrypt=true;"
. "TrustServerCertificate=false;LoginTimeout=30",
$config['SQL_USER'],
$config['SQL_PASSWORD'],
[
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::SQLSRV_ATTR_ENCODING => PDO::SQLSRV_ENCODING_UTF8,
]
);
$sql = "SELECT Name FROM (VALUES (1, 'Bike'), (2, 'Helmet')) AS Product(ProductId, Name) WHERE ProductId = ?";
$stmt = null;
try {
$stmt = $pdo->prepare($sql, [PDO::SQLSRV_ATTR_QUERY_TIMEOUT => 30]);
$productId = 1;
$stmt->bindParam(1, $productId, PDO::PARAM_INT);
foreach ([1 => 'Bike', 2 => 'Helmet'] as $productId => $expected) {
$stmt->execute();
$names = $stmt->fetchAll(PDO::FETCH_COLUMN);
if ($names !== [$expected]) {
throw new RuntimeException('Expected exactly one matching product.');
}
echo $names[0], PHP_EOL;
$stmt->closeCursor();
}
} finally {
$stmt = null;
$pdo = null;
}
Varje exempel skriver ut:
Bike
Helmet
Värden i en array som överförs till PDO:s execute() behandlas som PDO::PARAM_STR. När SQL kräver en heltalsparameter, såsom en parameteriserad TOP räkning, anropa bindValue() med PDO::PARAM_INT och anropa sedan execute() utan en array.
Bind värden utan att ändra frågan
En parametermarkör representerar ett värde, inte ett tabellnamn, kolumnnamn, nyckelord eller sorteringsriktning. Sätt inte citattecken runt.? För en dynamisk identifierare eller ASC/DESC, välj från en applikationsstyrd godkänd lista istället för att sammanfoga obetrodd text. För en IN lista, ange en markör per värde.
| Krav | SQLSRV | PDO_SQLSRV |
|---|---|---|
| Bind värden för en exekvering | Skicka parameterarrayen till sqlsrv_query(). |
Skicka en array till execute() efter prepare(), eller bind värden först. |
| Återanvänd en variabels aktuella värde | Inkludera &$variable i arrayen som tillhandahålls till sqlsrv_prepare(). |
Anropa bindParam() med variabeln, sedan execute() utan en array. PHP skickar variabeln via referens; lägg inte till & på anropsplatsen. |
| Bind ett värde vid bindningstillfället | Använd ett värde i parameterarrayen för exekveringen. | Anropa bindValue(); senare ändringar i den ursprungliga variabeln ändrar inte det bundna värdet. |
| Ange en heltalsindata | Använd en parameterbeskrivare med SQLSRV_PARAM_IN, SQLSRV_PHPTYPE_INT, och SQLSRV_SQLTYPE_INT när en explicit typ behövs. |
Bind med PDO::PARAM_INT. Positions- bindParam() och bindValue() index börjar vid 1, inte 0. |
Skicka SQL NULL |
Bind PHP null, med en explicit SQL-typ när operationen kräver det. |
Bind med PDO::PARAM_NULL. |
| Mottaga utgångsparametrar | Använd en refererad variabel med SQLSRV_PARAM_OUT eller SQLSRV_PARAM_INOUT. |
Använd bindParam() med PDO::PARAM_INPUT_OUTPUT och den obligatoriska typen och längden. |
Håll refererade variabler levande medan du exekverar den förberedda satsen. SQLSRV-programmet ändrar den begränsade variabeln istället för att ersätta parameterarrayen. PDO-programmet anropar execute() medvetet utan array efter bindning; blanda inte det mönstret med arraybaserad exekvering.
Utdata från lagrade procedurer kräver egen hantering, inklusive att slutföra procedurens resultatbearbetning. Dra inte slutsatser om output-parameter-beteende från dessa indata-enbart exempel. För mer information, se Användning av riktningsparametrar.
Konfigurera exekverings- och hämtbeteende
| Setting | SQLSRV | PDO_SQLSRV |
|---|---|---|
| Körningstimeout |
QueryTimeout i arrayen med kontoalternativ. |
PDO::SQLSRV_ATTR_QUERY_TIMEOUT på kopplingen eller instruktionen. |
| Namngivna resultatnycklar |
SQLSRV_FETCH_ASSOC skickades till sqlsrv_fetch_array(). |
PDO::FETCH_ASSOC som skickas till en hämtametod eller anges som anslutningens standardhämtningsläge. |
| Framåtriktade resultat | Standardmarkör; hämta i ordning. | standardmarkör; hämta i ordningsföljd. |
| Klientbuffrade resultat |
Scrollable => SQLSRV_CURSOR_CLIENT_BUFFERED. |
PDO::ATTR_CURSOR => PDO::CURSOR_SCROLL med PDO::SQLSRV_ATTR_CURSOR_SCROLL_TYPE => PDO::SQLSRV_CURSOR_BUFFERED. |
| Direkt kontra förberedd utförande | Ingen PDO-direktfrågaattribut. Välj det kombinerade sqlsrv_query() anropet eller det explicita förbered/exekveringsparet. |
PDO::SQLSRV_ATTR_DIRECT_QUERY väljer direkt exekvering istället för drivrutinens standardförberedda exekvering. |
| Parameteremulering | Ingen PDO-emuleringsattribut. |
PDO::ATTR_EMULATE_PREPARES är separat från attributet för direktfråga. Använd det inte som ersättning för att binda rätt typer. |
Exemplen returnerar små resultat.
fetchAll() laddar varje rad in i PHP-minnet; använd en fetch-loop för en stor resultatmängd. En fråga som inte ger några träffar är inte ett exekveringsfel. SQLSRV rapporterar slutet på resultatuppsättningen som null från sqlsrv_fetch_array(); PDO rapporterar det som false från fetch(). En SQL NULL i en hämtad rad förblir av typen PHP null.
Använd inte radräkning SELECT som ditt enda framgångstest. Framåtriktade kursorer ger inte samma radräkningsmöjligheter som buffrade eller rullbara kursorer. Hämta och räkna de rader du behöver, eller använd en separat fråga COUNT. För cursorspecifika alternativ och begränsningar, se Specificera en cursortyp och välj rader.
Hantera fel och slutföranderesultat
Kontrollera SQLSRV:s förberedelse-, körnings- och hämtningsoperationer separat. En lyckad förberedelse bevisar inte att exekveringen lyckas: servern kan avvisa parametervärden, åtkomsträttigheter eller satsen vid exekveringen. Fånga sqlsrv_errors() omedelbart när en operation returnerar false. SQLSRV behandlar många varningar som fel som standard, så inspektera hela samlingen.
För PDO kastar anslutningsfel och databasfel i undantagsläge PDOException. Inspektera dess SQLSTATE- och drivrutinsfelinformation i skyddade applikationsdiagnostik; returnera inte undantagstext eller inloggningsuppgifter i ett HTTP-svar.
prepare() Att lyckas är inte bevis på att satsen redan har körts.
| Symtom | Action |
|---|---|
| Varje exekvering använder det första värdet | För SQLSRV, bind en variabel med referens. För PDO, använd bindParam() för en variabel som ändras eller anrop bindValue() för varje nytt värde. |
En numerisk parameter förkastas, såsom ett TOP antal |
Ange den nödvändiga heltalstypen explicit. PDO execute([$value]) använder strängparametrar även om PHP-värdet är ett heltal. |
| Ett andra kommando rapporterar väntande resultat | Konsumera resultaten eller släpp dem innan du återanvänder anslutningen. För PDO, använd closeCursor() när du inte behöver det återstående resultatet. |
| Förväntade utgångsparametrar har inte ändrats | Slutför alla procedurresultat och verifiera parameterriktning, PHP-typ och ut-buffertlängd. |
| Minnesanvändningen ökar med resultatstorleken | Ersätt eller ersätt fetchAll() onödig klientbuffring med sekventiell hämtning. |
Exemplen frigör sina påståenden och frigör sina kopplingar i finally. De inleder inte en transaktion. Använd en explicit transaktion när flera skrivningar antingen måste committa tillsammans eller alla rulla tillbaka. Att återanvända ett förberett statement samlar inte skrivningarna i en transaktion.
Mål- och implementeringsskillnader
prepare/execute-API:erna och PHP-referensreglerna tillhör tilläggen och PHP/PDO. SQL-målet avgör vilka T-SQL-satser och typer som stöds, och ODBC utför den underliggande kommunikationen. Att förbereda en gång kan minska upprepat arbete, men det garanterar inte en specifik serverexekveringsplan eller hastighetsökning.
Inmatningsbindningsmönstren gäller för SQL Server, Azure SQL Database, Azure SQL Managed Instance, Azure Synapse Analytics, SQL Database i Microsoft Fabric och Warehouse i Microsoft Fabric. Dessa program använder SQL-autentisering till SQL Server. För Azure-onboarding, använd Microsoft Entra-autentisering i första hand. Båda Fabric-produkterna kräver det, och Warehouse kräver också att man inaktiverar Multiple Active Result Sets (MARS). Kontrollera det valda målets stöd för schemaändringar, transaktioner och lagrade procedurer innan du återanvänder SQL Server-kod. För skillnader i endpoint och autentisering, se Azure och Fabric connections.