PHP con PDO vs SQLSRV - comparison PDO and sql srv

 Para mí, la mejor manera de llegar a conocer un nuevo API es escribir algo de código. Así que, como un ejercicio de aprendizaje, convertí la aplicación de ejemplo que se incluye en la documentación del uso de la API sqlsrv procesal para usar el API orientada a objetos PDO (Los archivos convertidos se adjuntan a este mensaje. Usé la base de datos de ejemplo AdventureWorks2008, que puede descargar aquí .). Tal vez un mejor ejercicio sería reescribir toda la aplicación de ejemplo en un estilo orientado a objetos, pero quería permanecer centrado en cómo los cambios en el código de acceso de datos, así que voy a guardar el ejercicio más riguroso para más adelante.
Si usted ha escrito mucho código PDO contra otras bases de datos, la aplicación de ejemplo convertida en sí no puede ser tan interesante para usted. Sin embargo, mis observaciones de cómo comparar la API sqlsrv y la API de PDO podría ser interesante, así que voy a empezar por ahí. Tengo que decir que me quedé un poco decepcionado con las limitaciones de la interfaz PDO. Esto no es una crítica de este controlador PDO en particular, es sólo que la API sqlsrv ofrece algunas gran funcionalidad que no pueden ser fácilmente expuestos en la interfaz PDO. Esto es algo de lo que me di cuenta:
  • La funcionalidad streaming que está disponible en el API sqlsrv de procedimiento para la recuperación de datos no se puede aprovechar en la interfaz PDO. El API sqlsrv aprovecha PHP arroyos para que pueda enviar y recuperar datos  . Sin embargo, con el controlador PDO, no se puede recuperar datos como una secuencia. Dependiendo de la aplicación, esto puede tener un impacto significativo sobre el uso de memoria.
  • La funcionalidad ofrecida en sqlsrv_has_rows (determina si un conjunto de resultados tiene filas) y sqlsrv_num_rows (determina el número de filas de un conjunto de resultados) no está disponible a través de PDO. Hay soluciones, pero implican trayendo un conjunto de resultados (en parte o en su totalidad) en la memoria. Una vez más, esto puede no ser un problema dependiendo de la aplicación, pero podría ser.
  • La capacidad de moverse a través y recuperar un único campo en un conjunto de resultados de fila sólo se puede hacer con PDOStatement :: fetchObject al usar PDO. Con la API sqlsrv, la combinación de sqlsrv_fetch / sqlsrv_get_field no sólo le permite recuperar un solo campo, que le permite especificar el tipo de datos PHP y codificación cuando se recuperan los datos. Esto no está disponible a través de PDO. El PDOStatement :: fetchColumn método se acerca, pero no permite que usted se mueva cuando los campos de la fila actual.
  • La interfaz PDO no proporciona ninguna manera para el manejo de las advertencias que se provocan por la base de datos. Por defecto, el conductor sqlsrv trata la mayoría de las advertencias como errores para que puedan ser manejados . (Puede desactivar este comportamiento con el sqlsrv_configure función.) El controlador PDO sólo permite que los errores se manejan.
En el lado positivo, me gustó mucho el PDO :: lastInsertId método y la capacidad para lidiar con los errores de manejo de excepciones. En el código de ejemplo siguiente se puede ver cómo estas características hacen de código mucho más legible.
Tengo curiosidad por saber qué tan relevante o importante es esta información. ¿Son importantes para usted estas características sqlsrv (que no se aparecieron en la interfaz PDO)? ¿Qué tan importantes son? ¿Por qué? 
El resto de este post es para la gente que no se ha escrito mucho código PDO. Voy a mirar a una parte del código de la aplicación de ejemplo en la documentación del controlador sqlsrv y lo comparo con el mismo código escrito con el controlador PDO.

Conexión a una base de datos

No hay sorpresas aquí la conexión con la autenticación de Windows (y no hay sorpresas cuando se conecta con la autenticación de SQL Server). Tengo que hacer una llamada extra con DOP para establecer el modo de control de errores, pero me da la ventaja de trabajar con excepciones ahora.
sqlsrv
$ ServerName = "(local) \ sqlexpress"; 
$ ConnectionOptions = array ("Base de datos" => "AdventureWorks2008");
$ Conn = sqlsrv_connect ($ serverName, $ ConnectionOptions); 
if ($ conn === false) 
{Die (FormatErrors (sqlsrv_errors ()));}
PDO
$ ServerName = "(local) \ sqlexpress"; 

intentar 
{ 
$ Conn = new PDO ("sqlsrv: Servidor = $ serverName; Database = AdventureWorks2008", "", ""); 
$ Conn-> setAttribute (PDO :: ATTR_ERRMODE, PDO :: ERRMODE_EXCEPTION); 
} 
catch (Exception $ e) 
{ 
die (print_r ($ e-> getMessage ())); 
}

Recuperando datos

Dos cosas a destacar aquí:
  1. Con PDO, saqué todo el conjunto de resultados en la memoria para que pueda contar con los resultados.Con la API sqlsrv, puedo usar un cursor desplazable para contar los resultados en el servidor.
  2. La función FormatErrors envuelve una llamada a sqlsrv_errors en el ejemplo sqlsrv. En el código de PDO, tengo la ventaja de trabajar con excepciones.
sqlsrv
$ queryTerms = array ($ _POST ['query']); 
$ Tsql = "SELECT ProductID, Name, color, tamaño, ListPrice 
DE Production.Product 
Donde nombre LIKE '%' +? + '%' Y ListPrice> 0.0 "; 
$ CursorType = array ("desplazable" => SQLSRV_CURSOR_KEYSET); 
$ GetProducts = sqlsrv_query ($ conn, $ tsql, $ queryTerms, $ CursorType); 
if ($ GetProducts === false) 
{Die (FormatErrors (sqlsrv_errors ()));}
if (($ sqlsrv_has_rows GetProducts)) 
{ 
$ RowCount = sqlsrv_num_rows ($ GetProducts); 
BeginProductsTable ($ rowCount); 
while ($ row = sqlsrv_fetch_array ($ GetProducts, SQLSRV_FETCH_ASSOC))
{ 
PopulateProductsTable ($ row); 
} 
EndProductsTable (); 
} 
más 
{ 
DisplayNoProdutsMsg (); 
}
PDO
intentar 
{ 
$ queryTerms = array ($ _POST ['query']); 
$ Tsql = "SELECT ProductID, Name, color, tamaño, ListPrice 
DE Production.Product 
Donde nombre LIKE '%' +? + '%' Y ListPrice> 0.0 "; 
$ GetProducts = $ conn-> prepare ($ tsql); 
$ GetProducts-> execute ($ queryTerms); 
$ products = $ GetProducts-> fetchAll (PDO :: FETCH_ASSOC); 
$ ProductCount = count (productos $); 
if ($ ProductCount> 0) 
{ 
BeginProductsTable ($ ProductCount); 
foreach ($ productos como $ row) 
{ 
PopulateProductsTable ($ row); 
} 
EndProductsTable (); 
} 
más 
{ 
DisplayNoProdutsMsg (); 
} 
} 
catch (Exception $ e)
{ 
die (print_r ($ e-> getMessage ())); 
}

Insertar datos

Realmente, no hay sorpresas aquí, pero tenga en cuenta que la API sqlsrv permite que los datos se transmiten a la base de datos, mientras que la API de PDO no.
sqlsrv
. $ comentarios = "data :/ / text / plain", $ _POST ['comentarios']; 
$ = corriente fopen ($ comentarios, "r"); 
$ Tsql = "INSERT INTO Production.ProductReview (ProductID, 
ReviewerName, 
ReviewDate, 
EmailAddress, 
Clasificación, 
Comentarios) 
VALORES "(,,,,,??????);

$ params = array (& $ _POST ['productid'], 
& $ _POST ["Nombre"], 
date ("Ymd"), 
& $ _POST ['Email'], 
& $ _POST ['Rating'], 
& $ Stream);
$ InsertReview = sqlsrv_prepare ($ conn, $ tsql, $ params); 
if ($ insertReview === false) 
{Die (FormatErrors (sqlsrv_errors ()));}

if (sqlsrv_execute ($ insertReview) === false) 
{Die (FormatErrors (sqlsrv_errors ()));}
PDO
intentar 
{
$ Tsql = "INSERT INTO Production.ProductReview (ProductID, 
ReviewerName, 
ReviewDate, 
EmailAddress, 
Clasificación, 
Comentarios) 
VALORES "(,,,,,??????);

$ params = array (& $ _POST ['productid'], 
& $ _POST ["Nombre"], 
date ("Ymd"), 
& $ _POST ['Email'], 
& $ _POST ['Rating'], 
$ _POST ['Comentarios']); 
$ InsertReview = $ conn-> prepare ($ tsql); 
$ InsertReview-> execute ($ params); 
} 
catch (Exception $ e) 
{ 
die (print_r ($ e-> getMessage ())); 
}

Recuperar datos binarios

Una vez más, dos cosas a destacar aquí:
  1. Con la API sqlsrv, puedo usar la combinación de sqlsrv_fetch / sqlsrv_get_field para recuperar un solo campo y especificar que quiero recuperarla como una corriente PHP.
  2. La capacidad de trabajar con excepciones en el código de PDO para hacer más legible el código.
sqlsrv
$ Tsql = "SELECT LargePhoto 
DE Production.ProductPhoto AS p 
ÚNETE Production.ProductProductPhoto AS q 
EN p.ProductPhotoID = q.ProductPhotoID 
? DONDE ProductID = ";
$ params = array ($ _GET ['productId']);
$ Stmt = sqlsrv_query ($ conn, $ tsql, $ params); 
if ($ stmt === false) 
{ 
echo "Error en la ejecución de la sentencia </ br>."; 
morir (print_r (sqlsrv_errors (), true)); 
}
if (sqlsrv_fetch ($ stmt)) 
{ 

$ Image = sqlsrv_get_field ($ stmt, 
0, 
SQLSRV_PHPTYPE_STREAM (SQLSRV_ENC_BINARY)); 
fpassthru ($ imagen); 
} 
más 
{ 
echo "Error en la recuperación de datos </ br>."; 
morir (print_r (sqlsrv_errors (), true)); 
}
PDO
intentar 
{ 
$ Tsql = "SELECT LargePhoto 
DE Production.ProductPhoto AS p 
ÚNETE Production.ProductProductPhoto AS q 
EN p.ProductPhotoID = q.ProductPhotoID 
? DONDE ProductID = ";

$ Stmt = $ conn-> prepare ($ tsql); 
$ Stmt-> execute (array ($ _GET ['productId'])); 
$ BindColumn stmt-> (1, 
$ Imagen, 
PDO :: PARAM_LOB, 
0, 
PDO :: SQLSRV_ENCODING_BINARY); 
$ Stmt-> fetch (PDO :: FETCH_BOUND); 
echo $ imagen; 
} 
catch (Exception $ e) 
{ 
die (print_r ($ e-> getMessage ())); 
}

Inserción de datos binarios

La capacidad de trabajar con las excepciones y la PDO :: lastInsertId método de hacer el código PDO aquí mucho más legible. Con la API sqlsrv, no hay ninguna función integrada para obtener el último ID después de una inserción, por lo que necesita para ejecutar una consulta por lotes y trabajar a través del segundo conjunto de resultados.
sqlsrv
$ Tsql = "INSERT INTO Production.ProductPhoto (LargePhoto) 
VALORES (?); SELECT SCOPE_IDENTITY () AS Número de foto "; 
$ FileStream = fopen ($ _FILES ['file'] ['tmp_name'], "r"); 

$ UploadPic = sqlsrv_prepare ($ conn, $ tsql, array ( 
array (& $ fileStream, 
SQLSRV_PARAM_IN, 
SQLSRV_PHPTYPE_STREAM (SQLSRV_ENC_BINARY), 
SQLSRV_SQLTYPE_VARBINARY ('max')))); 

if ($ uploadPic === false) 
{Die (FormatErrors (sqlsrv_errors ()));} 

if (sqlsrv_execute ($ uploadPic) === false) 
{Die (FormatErrors (sqlsrv_errors ()));} 

$ Next_result = sqlsrv_next_result ($ uploadPic); 
if ($ next_result === false) 
{Die (FormatErrors (sqlsrv_errors ()));} 
if (sqlsrv_fetch ($ uploadPic) === false) 
{Die (FormatErrors (sqlsrv_errors ()));}
$ Número de foto = sqlsrv_get_field ($ uploadPic, 0);
$ Tsql = "Production.ProductProductPhoto ACTUALIZACIÓN 
SET ProductPhotoID =? 
? DONDE ProductID = ";
if (sqlsrv_query ($ conn, $ tsql, array ($ Número de foto, $ _POST ['productid'])) === false) 
{Die (FormatErrors (sqlsrv_errors ()));}
PDO
intentar 
{ 

$ Tsql = "INSERT INTO Production.ProductPhoto (LargePhoto) 
VALORES "(?); 
$ UploadPic = $ conn-> prepare ($ tsql); 
$ FileStream = fopen ($ _FILES ['file'] ['tmp_name'], "r"); 
$ UploadPic-> bindParam (1, 
$ FileStream, 
PDO :: PARAM_LOB, 
0, 
PDO :: SQLSRV_ENCODING_BINARY); 
$ UploadPic-> execute ();
$ Número de foto = $ conn-> lastInsertId (); 
$ Tsql = "Production.ProductProductPhoto ACTUALIZACIÓN 
SET ProductPhotoID =? 
? DONDE ProductID = "; 
$ AssociateIds = $ conn-> prepare ($ tsql); 
$ associateIds-> execute (array ($ Número de foto, $ _POST ['productid'])); 
} 
catch (Exception $ e) 
{ 
die (print_r ($ e-> getMessage ())); 
}
Una vez más, los dos archivos que componen la aplicación de ejemplo se encuentran en el archivo zip adjunto a este mensaje.
Eso es todo por este post. Por favor, hágamelo saber lo que piensa de esta versión previa del controlador de SQL Server para PHP 2.0. Me aseguraré de que el equipo de desarrollo escucha sus comentarios.

3 comentarios:

  1. Hola que tal ayudeme con algo, estoy trabajando con SqlSrv para mi base de datos y tengo un procedimiento almacenado pero cuando le creo el statement y el execute o ejecutandolo con el sqlsrv_query no me genera nada ni un error ni una ejecucion correcta del procedimiento es como si no se ejecutara, el procedimiento esta probado desde sql server y funciona cuando lo mando a correr. Tienes alguna respuesta a este tema.

    ResponderEliminar
    Respuestas
    1. Estimado Freddy,
      Espero que hayas encontrado la solución, de no ser así pongo los primeros indicios que yo verificaría para garantizar que todos los pasos los hago bien:

      1. Cadena de conexión
      2. comprobar conexion (de preferencia retirar cualquier try catch)
      3. ejecutar select simple. (funciona? la version de php tiene habilitado sqlsvr?)

      seguimos si hasta aqui todo funciona:

      3. cuanto tarda la sentencia en ejecutar desde sql?

      ver un poco de código para entender el problema no estaría mal, a veces un punto o una coma nos genera bastante lio.

      si encontraste la solución y quieres compartirla con nosotros sería genial.


      Saludos!

      Eliminar
  2. saludos, quiero acceder a una base de datos en sql server, desde php.
    he intentado varias cosas que he leido por internet y no me funciona,
    tengo wampserver 2.4
    php 5.4
    he copiado las librerias
    php_pdo_sqlsrv_54_ts.dll
    php_sqlsrv_54_ts.dll
    php_pdo_sqlsrv_7_ts_x64.dll
    php_sqlsrv_7_ts_x64.dll
    en el directorio php/ext pero aun asi al dar phpinfo() y aparece
    PDO
    PDO support enabled
    PDO drivers mysql, odbc, sqlite, sqlsrv

    tambien lo he intentado con odbc y tampoco puedo conectarme, a pesar que desde la sherramientas administrativas en odbc me aparece que pude conectarme bien.

    ResponderEliminar

Todos los comentarios son bien recibidos...

CommentFB