lunes, 16 de septiembre de 2013

Limitar las conexiones a unas IPs conocidas

En cuestión de seguridad en nuestras aplicaciones web, toda precaución es poca. La seguridad cuesta dinero, (una vez oí la frase "$eguridad se escribe con $", y tiene toda la razón). O cuesta tiempo, que viene a ser lo mismo pero en otra divisa. Por eso, no se puede hacer un sistema inmune a todas las posibilidades de ataque, siempre habrá alguien con más tiempo libre (o más dinero) que nosotros que se dedique a sabotear nuestra web y termine consiguiéndolo antes o después. Y si no, pues ya llegará la NSA y sacará la información que le dé la gana.

Pero esto no significa que nos tengamos que rendir sin más. Por eso, hay que intentar poner todas las medidas que se puedan implantar con un esfuerzo razonablemente ajustado al proyecto en el que se está trabajando. Algunas medidas son muy eficientes, pero tan costosas en tiempo o dinero que resultan inviables. Otras, en cambio, no requieren un gran esfuerzo y, aunque no sean la panacea, ayudan. Así que hay que intentar incluir estas "medidas de pequeño esfuerzo" que, combinadas, ayudan a hacer de nuestra web un sitio un poco más seguro.

Una de estas pequeñas medidas, que sólo se puede aplicar en entornos muy controlados (por ejemplo, una intranet, donde conocemos cuáles son los equipos que se van a conectar) consiste en limitar las conexiones a nuestro servidor a unas determinadas direcciones IP. Es verdad que esta restricción es relativamente fácil de saltar, pero si así nos quitamos algún "moscón", pues bienvenida sea. Hace un par de meses, mi amigo M me envió el código para hacer esto en PHP, pero por diversos motivos hasta ahora no había podido probarlo (y eso que se hace en 5 minutos, ¿eh?). Así que no seáis como yo y si os gusta, integradlo en vuestra intranet cuanto antes. Yo ya lo he hecho, y desde hoy los usuarios sólo deberían poder conectarse desde las IPs que yo permita. La única modificación que he hecho ha sido que en vez de una única dirección IP válida, he creado un array de ellas ($vip = array de IPs válidas).

Paso al código, que ya está bien de palabrería. Lo único que hay que hacer es esta función:

 1 function chequear_ips() {
 2     $vip = array("10.15.x.y", "10.15.x.z"...);
 3     if (!in_array($_SERVER['REMOTE_ADDR'], $vip)) {
 4         header("HTTP/1.1 401 Unauthorized Access Denied");
 5         header("Location: http://www.google.es/");
 6         exit(1);
 7     }
 8 }

y a continuación, al principio de cada página que queramos tener con acceso limitado, invocar a esta función. Lo más habitual será incluirlo en un fichero general y hacer el include_once correspondiente. Como veréis, en la línea 2 se van añadiendo las direcciones IP a las que queremos dar acceso.

Facil, ¿verdad?

Ejercicio: ¿cómo podemos saltarnos esta restricción?

martes, 27 de agosto de 2013

Botón predeterminado en formularios con un sólo campo

Lo que sigue puede parecer una tontería, pero esta "tontería" me ha hecho perder casi dos horas haciendo pruebas y buscando por la web, además de hacerme maldecir en todos los idiomas que conozco y alguno inventado.

Y la solución la he encontrado por casualidad, así que podría haber sido peor. Aquí os lo pongo por si os pasa algo así.

PRIMER CASO


Supongamos que queremos desarrollar un formulario HTML con UN PAR DE CAMPOS, que será procesado por una página en PHP (por simplificar, voy a hacer que la misma página de acción sea la que contiene al formulario). La tarea parece fácil, ¿verdad? Algo así:

Página f1.php
 1 <html>
 2 <head>
 3 <title>Form con 2 campos</title>
 4 </head>
 5 <body>
 6     <form action='f1.php' method='post'>
 7         Dato 1 <input type='text' name='dato1'>
 8         Dato 2 <input type='text' name='dato2'>
 9         <input type='submit' name='submit' value='Enviar'>
10     </form>
11 <?php
12 if (isset($_POST['submit'])) {
13     $dato1 = $_POST['dato1'];
14     $dato2 = $_POST['dato2'];
15     echo "Datos [$dato1] y [$dato2]";
16 }
17 ?>
18 </body>
19 <html>

Como véis, he obviado el control de errores en los datos, si están vacíos, etc...

Ahora lo probamos insertando un par de datos y terminamos PULSANDO con el ratón el botón "Enviar". El resultado es el mensaje de nuestro maravilloso algoritmo:


Fig. 1: Maravilloso algoritmo

Si a alguien le parece más cómodo (a mí sí) pulsar la tecla INTRO al terminar de introducir el dato 2, observará que el resultado es exactamente el mismo. Es decir, al navegador tanto le da que pulsemos INTRO como que hagamos clic en el botón "Enviar". Hasta aquí todo bien.

SEGUNDO CASO

Ahora hagamos una copia de esta página con un pequeño cambio, eliminando el dato 2 y dejando el código como sigue:

Página f2.php
 1 <html>
 2 <head>
 3 <title>Form con 1 campo</title>
 4 </head>
 5 <body>
 6  <form action='f2.php' method='post'>
 7   Dato 1 <input type='text' name='dato1'>
 8   <input type='submit' name='submit1' value='Enviar'>
 9  </form>
10 <?php
11 if (isset($_POST['submit1'])) {
12  $dato1 = $_POST['dato1'];
13  echo "Dato [$dato1]";
14 }
15 ?>
16 </body>
17 <html>

Si al insertar el dato 1 hacemos clic en el botón "Enviar", todo funciona como antes:
Fig. 2: Maravilloso algoritmo simplificado

Pero si en vez de eso, hacemos la opción cómoda, o sea, pulsar INTRO al terminar de teclear el dato 1, resulta que la salida que obtenemos es, extrañamente, esta:

Fig. 3: ¡Juro por Snoopy que yo he pulsado INTRO!

Es decir, el resultado es exactamente igual que si no hubiera pulsado nada, y simplemente hubiera recargado la página sin enviar datos.

Hmmm... bien raro, ¿no? Visto ahora aquí, con este par de ejemplos tan sencillos, la cosa está más o menos clara y parece muy fácil, pero a mí me ha tenido loco un buen rato. Yo quería, por supuesto, por orgullo profesional, que, aunque la página funcionara haciendo clic sobre el botón, también existiera la opción de pulsar INTRO al teclear el dato. Y, claro, esto no estaba funcionando como yo esperaba.

LA PISTA DE LA EXPLICACIÓN

Tras un buen rato de búsqueda, y leer en veinte mil sitios cómo se hace un formulario HTML y cómo se procesa, he encontrado este texto que me ha dado la pista:

Fig. 4 El botón SUBMIT no siempre es necesario (oh my god!)

Supongo que esto vendrá en la especificación oficial de HTML. Aunque siempre se recomienda leerla, reconozco que hacerlo puede ser muy tedioso, y no es habitual que casi nadie lo haga. Normalmente aprendes HTML siguiendo algún curso o tutorial y los detalles los vas conociendo en aquellos aspectos específicos que te van haciendo falta. En mi caso, hasta ahora siempre había trabajado con formularios de dos o más campos, así que no me había encontrado antes con este problema.


Bien. Entonces, he comprobado que en ese caso, cuando el formulario SOLO TIENE UN CAMPO con datos de entrada, el botón "Enviar" no se establece a nada, es decir, la condición

if (isset($_POST['submit1']))

se hace falsa.

LA SOLUCIÓN

Obviamente, pues en vez de comprobar si se ha establecido algún valor para el botón submit (o como comprobación ADICIONAL, combinada en un OR con la anterior), se puede comprobar si se ha introducido algún dato en el único campo, es decir, algo así:

if (isset($_POST['dato1']))

Y esta condición ya sí que se hace cierta en el caso de que se pulse INTRO al terminar de escribir el dato. De hecho, he comprobado que si elimino el botón submit del formulario, al pulsar INTRO también se hace el envío POST. Hmmm, ¿sería buena idea no poner un botón "Enviar" en un formulario con un único campo de texto? Por un lado, la interfaz de usuario se simplifica al máximo, pero por otro, puede despistar... no sé.

¿Y tú? ¿Has estado alguna vez perdiendo el tiempo por tonterías tan insignificantes como esta? ¿Hay que leerse las especificaciones al detalle antes de ponerse a currar, o hay que darse estos "coscorrones" de vez en cuando y meterlos en esa bolsa que llamamos experiencia?

Si te apetece, deja tus comentarios.

martes, 4 de junio de 2013

Codificación de caracteres en páginas web

Las codificaciones de caracteres son un quebradero de cabeza. Cuando hago una página web y al abrirla en un navegador empiezo a ver caracteres "extraños", me da mucha rabia. Sobre todo, porque demuestra que algo se está haciendo mal, y normalmente suelo ser yo, y no el ordenador, quien se está equivocando.

Fig. 1. ¡Qué rabia me dan estas cosas! ¡Y qué impresión más cutre da uno como programador!

¿Qué codificación hay que utilizar: UTF-8, ASCII, CP-1252, ISO-8859-1, ISO-8859-15...? Esto es para volverse loco. O para comerse las cerillas, que diría Llopis.

Preguntando a la autoridad en esta materia, el World Wide Web Consortium (W3C), esta es la cuestión (ver http://www.w3.org/International/questions/qa-choosing-encodings.es.php, cuya lectura os recomiendo encarecidamente, es muy reveladora).

y un poco más abajo dan la respuesta



Lo mejor es configurar el editor con el que se está trabajando para que todos los documentos se generen con esta codificación. En mi caso, últimamente estoy trabajando con el editor Komodo, para PHP. Esto se codifica aquí:

Edit / Preferences

También hay que tener cuidado, ya que aparte de la configuración genérica (para todos los ficheros), también se puede establecer una configuración específica para un fichero individual, el fichero actual. Esto se puede comprobar en el menú Edit / Current File Settings

Y se configura en esta pantalla


A veces, no basta sólo con esto. También es importante añadir la cabecera META adecuada a la página. En el ejemplo de la página mostrada arriba (la de la contraseña), la ñ se visualizaba erróneamente a pesar de estar codificada en UTF-8, y de que el navegador me la reconociera como tal (lo que se puede comprobar en el menú Ver / Codificación, tanto en IE como en Firefox, aunque esto puede tener pequeñas variaciones dependiendo del navegador y su versión)


¿Qué faltaba, pues? Pues faltaba añadir en la sección <head> esta etiqueta

<meta http-equiv="Content-Type" content="text/html; charset=utf-8">

Una vez añadida, ahora ya se ve bien.

Si la página no es HTML, sino que se genera, por ejemplo, un Excel, hay que añadir la cabecera pero cambiando el atributo "content" en función del tipo de contenido. Por ejemplo, generando un XLS con PHP, hay que añadir:

header("Content-type: application/vnd.ms-excel charset=utf-8");

Conclusión: como dice el W3C, SIEMPRE QUE SEA POSIBLE, USE UTF-8. (Pero no te olvides de la etiqueta META correspondiente).

Por supuesto, esta es la respuesta simple. Hay que considerar otros casos particulares, como por ejemplo, tener que mostrar datos de una base de datos que no está en UTF-8. En ese caso, a lo mejor conviene cambiar la codificación de esa página en concreto, o bien convertir los resultados utilizando las funciones de conversión entre codificaciones.

Yo tengo ahora mismo ese problema. En la misma página (codificada en UTF-8, y con el META correspondiente) tengo que mostrar información procedente de diferentes bases de datos. Claro, con unos registros se me muestran bien las tildes y con otros no.


¿Será por el origen de la información, que viene de diferentes bases de datos? En efecto, parece ser que esa es la causa. Las BD están codificadas con diferentes codificaciones. Para resolver esto, o sea, para convertir a UTF-8 datos que no están en dicha codificación, tenemos en PHP la función utf_encode. En mi caso, usarla consiste en cambiar, donde dice

$s1=odbc_result($rs,"s1");

debe decir

$s1=utf8_encode(odbc_result($rs,"s1"));

Una vez hecho, ya se ven bien los resultados:

Bueno, estoy seguro de que voy a tener que seguir peleándome un poco con las codificaciones, pero obligarme a escribir esto me ha hecho comprender mucho más claro el problema y las soluciones.

¡Feliz programación!

lunes, 29 de abril de 2013

Restaurar contraseña olvidada en Linux Mint


Segunda vez en tres meses que he olvidado la contraseña del equipo donde tengo instalado el Linux Mint.



De los procesos de restauración que he encontrado en Internet, este me ha parecido el más directo y efectivo. Muchas gracias a "livinlavidalinux".

http://livinlavidalinux.wordpress.com/2011/07/15/restaurar-contrasena-olvidada-en-linuxmint/

Como no quiero andar buscándolo cuando me vuelva a pasar (cosa altamente probable), aquí me pongo una versión resumida. Para entenderla, hacer un "man" de cada comando y leer detenidamente la ayuda.


1) Arrancar con un Live CD y abrir una terminal de consola
2) Ejecutar
$ sudo su -
# fdisk -l (para ver cuál es la partición del sistema, probablemente algo como /dev/sda1)
# mount /dev/sda1 /mnt (si la partición era otra diferente a sda1, poner la que sea correcta)
# chroot /mnt
# passwd root (la pedirá 2 veces)

También podemos cambiar la pass del usuario con el que queremos iniciar (root tiene deshabilitado el inicio de sesión gráfica en Linux Mint)
# passwd jose (la pedirá 2 veces)

Ale, ya podéis "jaquearle" el linux a vuestro hermano mayor, si es que al pobre no se le ha ocurrido cifrar el disco.

martes, 19 de febrero de 2013

Harry Potter y el increíble poder de las "no transacciones" o Cómo hacer inserciones masivas en SQLite3 sin pérdida de rendimiento

Andaba yo preocupado porque necesitaba hacer una copia local de unas tablas desde un servidor Informix a una BD local SQLite3. Todo esto desde una aplicación web hecha en PHP.

Ni corto ni perezoso, me lancé a la tarea. Mi primera idea fue hacer una consulta a la BD Informix y luego procesar cada registro, componer la correspondiente instrucción SQL INSERT y ejecutarla. Algo así


 1     $sql="SELECT * FROM $tabla where " . $where ;
 2     $rs=odbc_exec($conn, $sql); if (!$rs) {exit("$modname: Error en SQL [$sql]");}
 3  ...
 4  while (odbc_fetch_row($rs)) {
 5   ...
 6   $sql="insert into " . $tabla . "(" ...
 7   $res = $db_sqlite3->exec($sql);
 8   ...
 9  }//while

Pero me decepcioné cuando vi lo que se tardaba en hacer la copia

 Como véis, el primer bloque son 2.628 registros, y tarda 34"
El segundo bloque, 50.446 registros, tardó 11' 13", o sea, 673"

Fatal. Y eso que sólo estaba sacando una parte de los registros de la tabla. La tabla original tenía casi 90.000 registros, pero es que además necesitaba copiar otras tablas más grandes, incluso una de ellas con 8 millones de registros. Y a este paso, la copia iba a terminar para mi fecha de jubilación, aproximadamente.

La cosa quedó ahí aparcada un tiempo, y me desvié hacia otras cuestiones. El problema lo resolví de otra manera, pero me quedó la espinita clavada. ¿Por qué se tardaba tanto en hacer una copia, si yo había leído que SQLite3 era rapidísima? Estaba claro que no era cosa del Informix, ya que hacer una copia a un MDB de Access era muchísimo más rápido.

Y ahora llega el momento en el que entra mi gran amigo Miguel en acción. El otro día vino a verme al curro, y, mientras nos tomábamos un café, surgió el tema de SQLite3 y le conté mi problema.

Un par de horas después tenía un correo suyo en mi buzón, con enlaces a estas dos estupendas entradas:

Hola Jose

Tu problema de lentitud no parece que sea SOLO de indices. Como las BD de SQLite son ficheros hay otro problema añadido de bloqueo de ficheros.

Mira estos enlaces:

http://tech.vg.no/2011/04/04/speeding-up-sqlite-insert-operations/
http://blog.quibb.org/2010/08/fast-bulk-inserts-into-sqlite/

Después de leerlos, he comprendido la causa del problema. Básicamente, la cosa es que cada INSERT que se ejecuta dentro del bucle consiste en una transacción. SQLite3 se asegura de que se consolida en disco antes de dar por terminada la operación. Lo que se necesita en casos como este es ejecutar TODAS las instrucciones en una única transacción, y no cada una por separado. Esto es lo principal. Aparte, se pueden parametrizar ciertos valores en la BD para ayudar a mejorar el rendimiento:


1) Desactivar el modo de funcionamiento síncrono de SQLite3
 1 $db_sqlite3->exec("PRAGMA synchronous=OFF");

2) Iniciar una transacción justo antes del bucle, y cerrarla al salir

 1     $sql="SELECT * FROM $tabla where " . $where ;
 2     $rs=odbc_exec($conn, $sql); if (!$rs) {exit("$modname: Error en SQL [$sql]");}
 3  ...
 4  $db_sqlite3->exec("BEGIN TRANSACTION");
 5  while (odbc_fetch_row($rs)) {
 6   ...
 7   $sql="insert into " . $tabla . "(" ...
 8   $res = $db_sqlite3->exec($sql);
 9   ...
10  }//while
11  $db_sqlite3->exec("COMMIT TRANSACTION");

3) Otros PRAGMA para mejorar el rendimiento

 1  $db_sqlite3->exec("PRAGMA count_changes=OFF");
 2  $db_sqlite3->exec("PRAGMA journal_mode=MEMORY");
 3  $db_sqlite3->exec("PRAGMA temp_store=MEMORY");

4) Utilizar sentencias precompiladas (prepared statements)

Yo empleé en primer lugar la técnica 1) y el resultado en ese caso fue prometedor:


En este caso, el primer bloque tarda 19", frente a los 34" del primer caso. O sea, una mejora del 44%. Vamos bien.
El segundo bloque tardó 5' 41", o sea, 341", frente a los 673" del primer caso. O sea, una mejora del 49%. Bien, muy bien.

Pero la gran mejora vino al hacer lo segundo, y que he mencionado antes: abrir una transacción antes de entrar en el bucle, y cerrarla al salir. Resultados:



Primer bloque: 1" ¡Impresionante! Una mejora del 97%
Segundo bloque: 7". Una mejora del 99%. ¿Es para flipar o no lo es?

La opción 3) (otros PRAGMAS) también la probé, pero no hay variaciones significativas.

Y como no os lo voy a dar todo hecho, el tema de probar con las sentencias preparadas os lo dejo para que lo probéis vosotros.

Por supuesto, me encantaría oír vuestros comentarios.

jueves, 26 de julio de 2012

Windows XP. Activar la respuestas ICMP (ping) en un Dominio de Directorio Activo con Windows 2003 Server

Ayer intentaba comprobar si un equipo de la red estaba encendido. Le lancé un ping y no respondió (en la imagen he cambiado la IP, pero para el caso es lo mismo):
 

Esto me hizo pensar en un principio que estaba apagado. Pero al intentar acceder por VNC vi que el equipo en realidad sí que estaba encendido, pues me pedía la contraseña.


Así que entré en el equipo y le habilité las respuestas al ping. La ruta es
Inicio-> Configuracion -> Panel de Control -> Firewall de Windows, pestaña "Opciones avanzadas" -> ICMP -> Configuración, activar "Permitir solicitudes de Eco entrante"


Sin embargo, ya que este equipo está en un dominio, me preguntaba cómo podría hacer esto con una política que afectara a todos los equipos incluidos en el dominio, sin tener que pasar uno a uno haciendo esto. Esto es así porque tengo muchos otros equipos en el dominio, y no quiero tener que ir habilitando esto individualmente.

La respuesta detallada está aquí:
http://www.microsoft.com/latam/technet/articulos/smallbus/fwgrppol.mspx

Pero pondré un resumen aquí por si queréis ahorraros algo de tiempo:

Las Excepciones ICMP vienen desactivadas todas por defecto (configuración predeterminada = ninguna)

Así que hay que irse a uno de los controladores del dominio, abrir la consola (Inicio / Ejecutar / "mmc") y en el complemento "Directiva Default Domain Policy" hay que activar "Permitir solicitud de eco entrante". La ruta completa hasta esta entrada es:
Directiva Default Domain Policy -> Configuración del equipo -> Plantillas administrativas -> Red -> Conexiones de red -> Firewall de Windows -> Perfil del dominio, entrada "Firewall de Windows: permitir excepciones ICMP"

Una vez cambiada, entré en el equipo cliente y comprobé que ya aparecía esa opción marcada.

Nota: las capturas de pantalla corresponden a un controlador Windows 2003 Server, así que puede haber algunas diferencias con otras versiones.

viernes, 13 de abril de 2012

La inutilidad de la aplicación NewSID

Hoy he tenido un pequeño "debate" con un par de compañeros acerca del tema de problemas que tenemos en la empresa con una máquina clonada. Eso nos ha llevado al tema de los SID (identificadores usados en Windows para máquinas y cuentas de usuario) y la herramienta NewSID de Mark Russinovich (el famoso programador de sysinternals).

Russinovich se dio cuenta, cuando tuvo que actualizar la herramienta ante la aparición de Windows Vista, de que en realidad no era necesario obtener un nuevo SID en una máquina clonada. ¡Pero si eso llevaba haciéndose, sin cuestionarse, desde 1997! (y 12 años después descubrió que era innecesario).

Bueno, Sergio de los Santos, de Hispasec, lo explica mejor que yo, así que aquí tenemos el enlace para entender los detalles técnicos y el porqué de una herramienta que se vino utilizando por inercia psicológica, porque, como dice el autor, "todo el mundo asumió que otra persona sabría exactamente cuál era el problema"

El original, en inglés, escrito por el propio Russinovich:
http://blogs.technet.com/b/markrussinovich/archive/2009/11/03/3291024.aspx

Para quienes prefieran leerlo en español (yo, por ejemplo ;-)

Parte 1:
http://unaaldia.hispasec.com/2010/05/la-noticia-no-es-nueva-realmente-se.html

Parte 2:
http://unaaldia.hispasec.com/2010/05/es-casi-seguro-que-todos-los-usuarios-y.html

The power of myths.


Por cierto, dos curiosidades acerca de Russinovich: Una, que con ese nombre tan cirílico, resulta que nació en... ¡Salamanca! ¡Olé! Y la otra, que el año pasado publicó una novela: Zero Day.

Habrá que leerla.

miércoles, 7 de marzo de 2012

Descargar https://algo en IE - Cabeceras PHP

¡Buf! Bueno, me ha costado, pero al final ha salido. Resulta que tengo una aplicación web que genera en un momento dado un fichero de Excel. Cuando he habilitado la famosa "s" para añadir cifrado a las páginas, pasando del HTTP al HTTPS, pues resulta que el fichero Excel me lo descargaba bien con Firefox y con Chrome, pero se interrumpía la descarga con IE 9.

Tras unas 3 ó 4 horas leyendo, al final he detectado el problema, pero encontré una solución que tenía un error tipográfico y eso me llevó loco. La clave está en enviar una cabecera en la página que produce la página. Antes, tenía estas 2 cabeceras:

header("Content-type: application/vnd.ms-excel");
header("Content-Disposition:  filename=\"TABLA.XLS\";");

Ahora, he tenido que añadir esta tercera:

header("Cache-Control: max-age=1");

Básicamente, lo que eso hace es permitir que la página cifrada se cachee en un fichero temporal. Si no queremos que eso ocurra (poniéndonos un poco paranoicos con la seguridad, que es lo que me pasa a mí), pues pedimos que el tiempo máximo de duración del fichero cacheado sea de 1 segundo. 

Si queréis la explicación detallada, aquí la mejor página de las que he leído con la explicación:



viernes, 13 de enero de 2012

SQL - Seleccionar sólo las primeras N filas

El otro día metí la pata haciendo un SELECT genérico de una tabla de la que desconocía el tamaño. El procesador se puso casi al 100% durante 5 minutos, y el disco duro no dejaba de rascar. Los usuarios que compartían (sufrían) conmigo el acceso a la BD estuvieron experimentando un rendimiento pésimo durante esos 5 minutos (bueno, los pocos que intentaron trabajar en ese período), que a mí se me hicieron eternos. El problema es que la tabla tenía casi 2 millones de registros y esto fue er... bueno... "un poco" pesado. Encima, el resultado fue a una página web, así que os podéis imaginar el tamaño del "ascensor" (el recuadro) de la barra de desplazamiento. Casi no se podía pinchar en él de pequeño que se quedó.

El caso es que me interesaba simplemente ojear el contenido de esa tabla, me hubiera bastado con recuperar unos pocos registros, digamos, unos 100 para hacerme una idea de su contenido. Bueno, pues eso se puede hacer sin necesidad de poner filtros a los datos (cosa que en su momento no podía hacer pues no tenía ni idea de los campos y datos de la tabla).

En ORACLE
SELECT * FROM TABLA WHERE ROWNUM <100

En MS SQL Server y en SYBASE

SELECT TOP 10 * FROM TABLA

En INFORMIX
SELECT FIRST 100 * FROM TABLA


Actualización 02/09/2014 para incluir SQLite3, que me ha hecho falta hoy. Hay que añadir LIMIT(N) al final de la consulta

En SQLite3
SELECT * FROM TABLA LIMIT(100)

Si hubiera cláusulas WHERE u ORDER BY, hay que poner la opción LIMIT al final de todo, o sea:

SELECT * FROM TABLA WHERE CONDICION ORDER BY CAMPO LIMIT(100)

viernes, 16 de diciembre de 2011

Related Posts Plugin for WordPress, Blogger...