tag:blogger.com,1999:blog-1621842497834108372024-03-05T11:51:39.526+01:00Picar PiezasConsejos y opciones de un fustigado programador.
Ayudas y consejos de Programación en PowerBuilder y SQLElPaterhttp://www.blogger.com/profile/16022680446046453947noreply@blogger.comBlogger10125tag:blogger.com,1999:blog-162184249783410837.post-45916360320833606582015-08-07T14:26:00.000+02:002015-08-07T14:26:52.108+02:00Unidades, formatos y lógica absurdaLuego dicen que por qué es un lío trabajar con sistemas informáticos válidos para cualquier parte del mundo...<br />
<br />
Cuando el sentido común es el menos común de los sentidos, y la lógica brilla por su ausencia.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjmF10DyqiB2koQ6cUhJjt-VWRHKR3IniE4kchJNy1fOcX3hwJ-e_k4YBHCQVBqKKAaB6XwA5K48fmg23HCxSCnIsiTYMJhZVI56ZfL_s4EpBPIDos91saDh8kJLeahKVDTY-0f7ZRHhik/s1600/4vNSol.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="606" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjmF10DyqiB2koQ6cUhJjt-VWRHKR3IniE4kchJNy1fOcX3hwJ-e_k4YBHCQVBqKKAaB6XwA5K48fmg23HCxSCnIsiTYMJhZVI56ZfL_s4EpBPIDos91saDh8kJLeahKVDTY-0f7ZRHhik/s640/4vNSol.jpg" width="640" /></a></div>
<br />
En fin, algo de humor para alegrar el verano. ;)
Nos vemos!ElPaterhttp://www.blogger.com/profile/16022680446046453947noreply@blogger.com0tag:blogger.com,1999:blog-162184249783410837.post-81055230731444129092015-07-20T15:54:00.000+02:002015-07-21T11:02:54.147+02:00Algoritmos de ordenaciónHoy he encontrado una página bastante útil que define perfectamente los tipos de <b>Algoritmos de Ordenación</b> que usamos los programadores.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjlhgMb_ICGP0VeWHUh-zVmiSoyPwdlRL4aYQ0dzYZtHD00vMdBSoHcq0kbZ2AQ5HULP3hOTsp1iq_nyZF1cwoC3kmoe4odQCr1WRv4cmnpMxj_puaXgVKGVcdPL0JfwBVNPJ9d6c8Iio4/s1600/sorting.gif" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="324" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjlhgMb_ICGP0VeWHUh-zVmiSoyPwdlRL4aYQ0dzYZtHD00vMdBSoHcq0kbZ2AQ5HULP3hOTsp1iq_nyZF1cwoC3kmoe4odQCr1WRv4cmnpMxj_puaXgVKGVcdPL0JfwBVNPJ9d6c8Iio4/s640/sorting.gif" width="640" /></a></div>
<span id="goog_795492099"></span><span id="goog_795492100"></span><br />
<div style="text-align: center;">
<b><a href="http://www.sorting-algorithms.com/">http://www.sorting-algorithms.com/</a></b></div>
<br />
A modo de traducción:<br />
<br />
<div style="margin-bottom: 2ex; padding: 0px;">
Esta página muestra 8 algoritmos distintos de ordenación en 4 condiciones iniciales distintas. La visualización pretende varias cosas:</div>
<ul style="list-style: square outside; margin: 0px 0px 2ex 20px; padding: 0px;">
<li style="margin: 0px 0px 0.5ex; padding: 0px;">Mostraros cómo opera cada algoritmo.</li>
<li style="margin: 0px 0px 0.5ex; padding: 0px;">Demostrar que no hay un algoritmo mejor.</li>
<li style="margin: 0px 0px 0.5ex; padding: 0px;">Mostrar las ventajas y desventajas de cada algoritmo.</li>
<li style="margin: 0px 0px 0.5ex; padding: 0px;">Demostrar que el comportamiento asintótico no siempre es el factor decisivo en la elección de uno u otro algoritmo. </li>
<li style="margin: 0px 0px 0.5ex; padding: 0px;">Demostrar que la condición inicial (orden de entrada y distribución de clabes) afecta al rendimiento tanto como la elección del algoritmo.</li>
</ul>
<div style="margin-bottom: 2ex; padding: 0px;">
El algoritmo de ordenación ideal debería tener las siguientes propiedades:</div>
<ul style="list-style: square outside; margin: 0px 0px 2ex 20px; padding: 0px;">
<li style="margin: 0px 0px 0.5ex; padding: 0px;">Estable: Las claves idénticas no se reordenan.</li>
<li style="margin: 0px 0px 0.5ex; padding: 0px;">Opera en el lugar, lo que requiere O (1) espacio adicional.</li>
<li style="margin: 0px 0px 0.5ex; padding: 0px;">El peor caso O (n · lg (n)) de comparaciones clave.</li>
<li style="margin: 0px 0px 0.5ex; padding: 0px;">El peor caso O (n) de intercambios.</li>
<li style="margin: 0px 0px 0.5ex; padding: 0px;">Adaptativo: Acelera a O (n) cuando los datos se encuentran casi ordenados o cuando quedan pocas claves únicas.</li>
</ul>
<h3>
Conclusión final</h3>
<div style="margin-bottom: 2ex; padding: 0px;">
<blockquote class="tr_bq">
<b><span style="font-size: large;">No existe un algoritmo que tenga todas estas propiedades, y la elección de uno u otro algoritmo de ordenación depende de la propia aplicación que estemos desarrollando.</span></b></blockquote>
<b><span style="font-size: large;"><br /></span></b>
Como link instructivo que me ha cautivado, este video de YouTube:<br />
<div class="separator" style="clear: both; text-align: center;">
<iframe allowfullscreen="" class="YOUTUBE-iframe-video" data-thumbnail-src="https://i.ytimg.com/vi/kPRA0W1kECg/0.jpg" frameborder="0" height="400" src="https://www.youtube.com/embed/kPRA0W1kECg?feature=player_embedded" width="600"></iframe></div>
<br /></div>
ElPaterhttp://www.blogger.com/profile/16022680446046453947noreply@blogger.com0tag:blogger.com,1999:blog-162184249783410837.post-77749028046905565122013-09-09T18:30:00.001+02:002016-04-25T09:05:34.370+02:0015 consejos para Optimizar nuestras consultas SQL<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiRxoeeycTTjZEPVCeDLSC7Gq82LU-653K8VUKMG2oXmyfRuh5oIuMCtfjxpeHdB0Bb79QKU_e493LZB_yhfaRYyfXvsdApMb6vordeK1U5peOxddkRRhy-uE5kB8r08bNi7D0h2qsB4F0/s1600/website-optimization_64.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiRxoeeycTTjZEPVCeDLSC7Gq82LU-653K8VUKMG2oXmyfRuh5oIuMCtfjxpeHdB0Bb79QKU_e493LZB_yhfaRYyfXvsdApMb6vordeK1U5peOxddkRRhy-uE5kB8r08bNi7D0h2qsB4F0/s1600/website-optimization_64.png" /></a></div>
Vamos a ver 15 formas de optimizar nuestras consultas SQL. Muchas son comunes y conocidas por todos los que nos dedicamos a esto. Otras quizá sean menos obvias y os sirvan de ayuda. Son simples instrucciones para que el <span style="color: #274e13;"><b>DMBS</b> </span>("DataBase Management System") -o como lo llamamos en mi entorno, el Motor de la Base de Datos- realice sus operaciones más rápido. <br />
<br />
<h3>
1.- Índices</h3>
Indexar nuestras columnas es una de las maneras más comunes y sencillas de optimizar nuestras consultas. Sin embargo, se debe tener un profundo conocimiento sobre cómo funciona el indexado en cada DMBS para utilizar correctamente sus índices. Dicho de otro modo: crear índices sin sentido, sencillos o absurdos sin comprender exactamente cómo funciona nuestra Base de Datos puede tener justamente el efecto contrario al deseado en nuestras consultas, y hacer que funcionen aún más lentas.
<br />
<h3>
2.- Símbolos Operacionales</h3>
Los símbolos operacionales como >,<,=,!=, etc. son muy útiles en nuestras consultas. Se pueden optimizar algunas consultas si la columna con el símbolo operacional en cuestión esta indexada. Por ejemplo: <code><span class="Apple-style-span" style="color: blue;">SELECT</span> * <span class="Apple-style-span" style="color: blue;">FROM</span> <span class="Apple-style-span" style="color: red;">MI_TABLA</span> <span class="Apple-style-span" style="color: blue;">WHERE</span> COLUMNA <span class="Apple-style-span" style="color: blue;">></span> 16 ;</code>Esta consulta no está optimizada, ya que el motor de la Base de Datos debe buscar el valor 16, y DESPUÉS escanear hacia delante del 16 y por detrás. <code><span class="Apple-style-span" style="color: blue;">SELECT</span> * <span class="Apple-style-span" style="color: blue;">FROM</span> <span class="Apple-style-span" style="color: red;">MI_TABLA</span> <span class="Apple-style-span" style="color: blue;">WHERE</span> COLUMNA <span class="Apple-style-span" style="color: blue;">>=</span> 17 ;</code>De este modo, el DMBS debe saltar directamente a los valores mayores a 16. Es casi la misma manera, pero evitas que el DMBS tenga que comparar también los valores menores para ver si entran en la query.<br />
<a name='more'></a><br />
<h3>
3.- Comodines</h3>
En SQL, el comodín se nos presenta con el símbolo ‘%’ (se puede ver un truco acerca de él en mi artículo anterior <a href="http://picarpiezas.blogspot.com.es/2013/06/buscar-el-tanto-por-ciento-en-un-like.html">Buscar el tanto por ciento (%) en un LIKE de una SELECT</a>). Usar comodines ralentiza bastante nuestras consultas, especialmente si la tabla en la que buscamos en bastante grande. Se pueden optimizar dichas consultas si podemos permitirnos poner el comodín únicamente como comodín-sufijo, en vez de como comodín-prefijo o como comodín-total.<br />
<br />
#Comodín-total:<br />
<code><span class="Apple-style-span" style="color: blue;">SELECT</span> * <span class="Apple-style-span" style="color: blue;">FROM</span> <span class="Apple-style-span" style="color: red;">MI_TABLA</span> <span class="Apple-style-span" style="color: blue;">WHERE</span> COLUMNA <span class="Apple-style-span" style="color: blue;">LIKE</span> <span class="Apple-style-span" style="color: purple;">'%manzana%'</span>;</code><br />
#Comodín-sufijo:<br />
<code><span class="Apple-style-span" style="color: blue;">SELECT</span> * <span class="Apple-style-span" style="color: blue;">FROM</span> <span class="Apple-style-span" style="color: red;">MI_TABLA</span> <span class="Apple-style-span" style="color: blue;">WHERE</span> COLUMNA <span class="Apple-style-span" style="color: blue;">LIKE</span> <span class="Apple-style-span" style="color: purple;">'manzana%'</span>;</code><br />
#Comodín-prefijo:<br />
<code><span class="Apple-style-span" style="color: blue;">SELECT</span> * <span class="Apple-style-span" style="color: blue;">FROM</span> <span class="Apple-style-span" style="color: red;">MI_TABLA</span> <span class="Apple-style-span" style="color: blue;">WHERE</span> COLUMNA <span class="Apple-style-span" style="color: blue;">LIKE</span> <span class="Apple-style-span" style="color: purple;">'%manzana'</span>;</code><br />
Esta columna debe estar indexada para que se aplique algo de optimización.
P.D: Hacer un comodín-total en una tabla con varios millones de registros puede suponer que tires abajo la Base de Datos.<br />
<h3>
4.- Operador NOT</h3>
Intenta evitar todo lo posible el operador NOT en SQL. Es mucho más rápido buscar por un valor exacto (operador positivo) como por ejemplo un LIKE, IN, EXISTS o el símbolo operacional =, en vez de usar un operador negativo como NOT LIKE, NOT IN, NOT EXIST o el símbolo != . Usar un operador negativo provoca que la búsqueda tenga que recorrer cada línea por separado identificar los que no son o que existen dentro de la tabla. Sin embargo, usando un operador positivo, la búsqueda para en cuanto se encuentra el resultado. <br />
<h3>
5.- COUNT Vs EXIST</h3>
Muchos de nosotros solemos usar el operador COUNT para determinar si existe un dato en particular.
<code><span class="Apple-style-span" style="color: blue;">SELECT</span> COLUMNA <span class="Apple-style-span" style="color: blue;">FROM</span> <span class="Apple-style-span" style="color: red;">MI_TABLA</span> <span class="Apple-style-span" style="color: blue;">WHERE</span> <span class="Apple-style-span" style="color: blue;">COUNT(</span>COLUMNA<span class="Apple-style-span" style="color: blue;">)</span> <span class="Apple-style-span" style="color: blue;">></span> 0 ;</code>
Este sistema es muy malo, ya que el COUNT debe contar cada registro de la tabla para ver cuantos hay. La mejor alternativa es usar el operador EXISTS, que para en cuanto encuentra el primer registro que concuerda con la búsqueda, sin tener que contarlos todos. Por lo tanto, existe.<br />
<h3>
6.- Comodín Vs Substr</h3>
Muchos desarrolladores indexamos nuestras tablas. Por lo tanto, si una columna en particular está indexada, es mejor usar un comodín para buscar en ella en vez de un Substr. El comodín usará el índice, el Substr no.
<br />
<br />
#MAL<br />
<code><span class="Apple-style-span" style="color: blue;">SELECT</span> * <span class="Apple-style-span" style="color: blue;">FROM</span> <span class="Apple-style-span" style="color: red;">MI_TABLA</span> <span class="Apple-style-span" style="color: blue;">WHERE</span> <span class="Apple-style-span" style="color: blue;">Substr(</span>COLUMNA<span class="Apple-style-span" style="color: blue;">, 1, 1)</span> <span class="Apple-style-span" style="color: blue;">=</span> <span class="Apple-style-span" style="color: purple;">'pepito'</span> ;</code>
Esta consulta hará Substr a cada registro de la tabla individualmente para rastrear el valor ‘pepito’. Pero del siguiente modo:<br />
<br />
#MEJOR<br />
<code><span class="Apple-style-span" style="color: blue;">SELECT</span> * <span class="Apple-style-span" style="color: blue;">FROM</span> <span class="Apple-style-span" style="color: red;">MI_TABLA</span> <span class="Apple-style-span" style="color: blue;">WHERE</span> COLUMNA <span class="Apple-style-span" style="color: blue;">=</span> <span class="Apple-style-span" style="color: purple;">'pepito%'</span> ;</code><br />
La consulta con comodín corre mucho más rápido en el DBMS. Ejemplo:<br />
#BUSCAR TODAS LAS FILAS QUE EL PRIMER CARACTER ES 'E'<br />
<code><span class="Apple-style-span" style="color: blue;">SELECT</span> * <span class="Apple-style-span" style="color: blue;">FROM</span> <span class="Apple-style-span" style="color: red;">MI_TABLA</span> <span class="Apple-style-span" style="color: blue;">WHERE</span> COLUMNA <span class="Apple-style-span" style="color: blue;">=</span> <span class="Apple-style-span" style="color: purple;">'E%'</span> ;</code> <br />
<h3>
7.- Columna con Índice Único</h3>
Algunas Bases de Datos (como MySQL) buscan mucho mejor en columnas que son únicas e indexadas (unique & indexed). Por esto mismo, es mejor recordar indexar por estas columnas que son únicas. Y si la columna es realmente de este modo, declararlas así. Aún así, si la columna nunca se va a usar para propósitos de búsqueda, no hay motivos para indexar esta columna en concreto, aunque se quede como única. <br />
<h3>
8.- Funciones Max y Min</h3>
Las funciones Max y Min buscan el valor Máximo y Mínimo de una columna. <strike>Podemos optimizar estas búsquedas colocando un índice en esas columnas concretas.</strike> Corrección: Podemos usar Max y Min en columnas que ya están indexadas. Pero si dicha columna se usa frecuentemente, tener un índice además debería ayudar a acelerar dichos operadores Max y Min. Decidir si poner un índice sólo para acelerar los Max y Min no es aconsejable. Los índices son caros de mantener, y crear indices sólo para acelerar Max y Min es una locura. Es como sacrificar el bosque entero por un único árbol. <br />
<h3>
9.- Tipos de Datos</h3>
Se deben usar los tipos de datos más eficientes (más pequeños) siempre que sea posible. Es innecesario, y algunas veces hasta peligroso, proporcionar tipos de datos enormes cuando se pueden solucionar un problema con tipos más pequeños. Por ejemplo, usando los tipos de datos enteros más pequeños para así tener tablas más pequeñas. MEDIUMINT es a veces una elección mejor que un INT, porque una columna MEDIUMINT uses 25% menos de espacio en disco. Por otro lado, VARCHAR es mejor que LONGTEXT para almacenar texto como un email, unas observaciones o pequeños detalles. <br />
<h3>
10.- Índices Primarios</h3>
La primera columna que se use para un indexado debe ser lo más corta posible. Esto hace que la identificación de cada fila sea más sencilla y eficiente por parte del DBMS. <br />
<h3>
11.- Indexado de cadenas</h3>
No es necesario indexar toda la cadena cuando en su lugar se pueden indexar un prefijo o sufijo de la cadena. Es aconsejable llevar a cabo dicha indexación especialmente si el prefijo o sufijo de la cadena proporciona un identificador único para la cadena.<br />
Los índices más cortos son más rápidos, no sólo debido a que requieren menos espacio en disco, sino porque también evitan que se acceda a ellos menos veces y a la caché de índice, y por lo tanto menos búsquedas en disco.<br />
<h3>
12.- Limitar el resultado</h3>
Otro método muy común para optimizar tu query es minimizar el número de filas devueltas. Si en una tabla tienes varios billones de registros y lanzas una simple pero potente query sin limitación, puedes echar a bajo la Base de Datos entera.
<code><span class="Apple-style-span" style="color: blue;">SELECT</span> * <span class="Apple-style-span" style="color: blue;">FROM</span> <span class="Apple-style-span" style="color: red;">MI_TABLA</span> ;</code>
No seas perezoso. Si sólo necesitas una cantidad de registros, trata de limitar el resultado. De esta forma no sólo serás más eficaz, sino que ayudarás a minimizar el daño que puede ocasionar un ataque por SQL de este tipo.
<code><span class="Apple-style-span" style="color: blue;">SELECT</span> * <span class="Apple-style-span" style="color: blue;">FROM</span> <span class="Apple-style-span" style="color: red;">MI_TABLA</span> <span class="Apple-style-span" style="color: blue;">WHERE</span> 1 <span class="Apple-style-span" style="color: blue;">LIMIT</span> 10 <b><span style="color: #38761d;">(Sólo en MySQL)</span></b> ;</code> <br />
<h3>
13.- Usa el Valor por Defecto</h3>
Si usas MySQL u Oracle, aprovéchate de la ventaja que tienen estas Bases de Datos por el hecho de tener columnas con valores por defecto. Inserta valor explícitamente únicamente cuando sean diferentes de los valores por defecto. Esto reduce el análisis (parsing) que MySQL u Oracle deben hacer y mejora la velocidad de los INSERTS.<br />
<h3>
14.- Subquery en un IN</h3>
Muchos de nosotroso usamos una sub-consulta (o subquery) dentro de un operador IN, tal que así:<br />
<code><span class="Apple-style-span" style="color: blue;">SELECT</span> * <span class="Apple-style-span" style="color: blue;">FROM</span> <span class="Apple-style-span" style="color: red;">MI_TABLA</span> <span class="Apple-style-span" style="color: blue;">WHERE</span> COLUMNA <span class="Apple-style-span" style="color: blue;">IN ( </span><span class="Apple-style-span" style="color: blue;">SELECT</span> COLUMNA2 <span class="Apple-style-span" style="color: blue;">FROM</span> <span class="Apple-style-span" style="color: red;">MI_TABLA2</span> <span class="Apple-style-span" style="color: blue;">)</span>;</code>
Hacer esto es muy caro para el DBMS porque la consulta SQL debe evaluar la query exterior antes que la interior. En vez de esto, podemos usar lo siguiente:<br />
<code><span class="Apple-style-span" style="color: blue;">SELECT</span> * <span class="Apple-style-span" style="color: blue;">FROM</span> <span class="Apple-style-span" style="color: red;">MI_TABLA</span>, (<span class="Apple-style-span" style="color: blue;">SELECT</span> COLUMNA2 <span class="Apple-style-span" style="color: blue;">FROM</span> <span class="Apple-style-span" style="color: red;">MI_TABLA2</span>) <span class="Apple-style-span" style="color: blue;">as</span> dummytable
<span class="Apple-style-span" style="color: blue;">WHERE </span>dummytable.COLUMNA2 = <span class="Apple-style-span" style="color: red;">MI_TABLA</span>.COLUMNA;</code><br />
Usando una dummytable (o tabla tonta) es mejor que usar un operador IN para hacer una sub-consulta. Como alternativa, un operador EXIST también es mejor.<br />
<h3>
15.- UNION en vez de OR</h3>
Los índices pierden su velocidad cuando se usan en situaciones de OR (en MySQL al menos)
<code><span class="Apple-style-span" style="color: blue;">SELECT</span> * <span class="Apple-style-span" style="color: blue;">FROM</span> <span class="Apple-style-span" style="color: red;">MI_TABLA</span> <span class="Apple-style-span" style="color: blue;">WHERE</span> COLUMNA_1 <span class="Apple-style-span" style="color: blue;">=</span> <span class="Apple-style-span" style="color: purple;">'pepito'</span> <span class="Apple-style-span" style="color: blue;">OR</span> COLUMNA_2 <span class="Apple-style-span" style="color: blue;">=</span> <span class="Apple-style-span" style="color: purple;">'manolito'</span>;</code><br />
Si hacemos la query anterior usando 2 consultas con UNION, estas sí usarán sus índices.
<code><span class="Apple-style-span" style="color: blue;">SELECT</span> * <span class="Apple-style-span" style="color: blue;">FROM</span> <span class="Apple-style-span" style="color: red;">MI_TABLA</span> <span class="Apple-style-span" style="color: blue;">WHERE</span> COLUMNA_1 <span class="Apple-style-span" style="color: blue;">=</span> <span class="Apple-style-span" style="color: purple;">'pepito'</span><br />
<span class="Apple-style-span" style="color: blue;"><b>UNION</b></span><br />
<span class="Apple-style-span" style="color: blue;">SELECT</span> * <span class="Apple-style-span" style="color: blue;">FROM</span> <span class="Apple-style-span" style="color: red;">MI_TABLA</span> <span class="Apple-style-span" style="color: blue;">WHERE</span> COLUMNA_2 <span class="Apple-style-span" style="color: blue;">=</span> <span class="Apple-style-span" style="color: purple;">'manolito'</span></code><br />
De hecho, corre más rápido.<br />
<h3>
Sumario</h3>
Estos consejos de optimización no garantizan que tus consultas/querys de SQL no se conviertan en cuellos de botella. Tampoco se garantiza que esas consultas que tienes pesadas, enormes y lentas sean inmediatas. Pero sí pueden hacer que se aligeren un poco. Parar hacerlas perfectas, se requiere mucho más análisis comparativo y conocimientos más profundos para optimizarlas a nivel máximo.<br />
Este artículo ha sido traducido y adaptado de un documento que encontré por internet hace tiempo, y me lo guardé. Se puede consultar el original y mandar los agradecimientos en este link: <a href="http://www.docstoc.com/docs/69264261/15-Ways-to-Optimize-Your-SQL-Queries">http://www.docstoc.com/docs/69264261/15-Ways-to-Optimize-Your-SQL-Queries</a>ElPaterhttp://www.blogger.com/profile/16022680446046453947noreply@blogger.com8tag:blogger.com,1999:blog-162184249783410837.post-31051183790908808322013-06-05T11:30:00.002+02:002015-05-12T13:09:23.390+02:00Buscar el tanto por ciento (%) en un LIKE de una SELECT<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiRxoeeycTTjZEPVCeDLSC7Gq82LU-653K8VUKMG2oXmyfRuh5oIuMCtfjxpeHdB0Bb79QKU_e493LZB_yhfaRYyfXvsdApMb6vordeK1U5peOxddkRRhy-uE5kB8r08bNi7D0h2qsB4F0/s1600/website-optimization_64.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiRxoeeycTTjZEPVCeDLSC7Gq82LU-653K8VUKMG2oXmyfRuh5oIuMCtfjxpeHdB0Bb79QKU_e493LZB_yhfaRYyfXvsdApMb6vordeK1U5peOxddkRRhy-uE5kB8r08bNi7D0h2qsB4F0/s1600/website-optimization_64.png" /></a></div>
Hoy nos hemos encontrado con un caso curioso. Necesitabamos buscar en una tabla de la Base de Datos los textos que contuvieran el símbolo de tanto por ciento (%).<br />
<br />
En un principio no parecía problemático... hasta que te pones a escribir la SELECT en SQL y pones:<br />
<br />
<code><span class="Apple-style-span" style="color: blue;">SELECT</span> * <span class="Apple-style-span" style="color: blue;">FROM</span> <span class="Apple-style-span" style="color: red;">MI_TABLA</span><br />
<span class="Apple-style-span" style="color: blue;">WHERE</span> DESCRIPCION <span class="Apple-style-span" style="color: blue;">LIKE</span> <span class="Apple-style-span" style="color: purple;">'%cosa%'</span> <span class="Apple-style-span" style="color: blue;">;</span></code>
<br />
Como sabemos, esta consulta traería todos las filas que en descripción contenga la palabra 'cosa', en cualquier parte de su texto. Pero no queremos buscar 'cosa', queremos buscar el tanto por ciento (%).<br />
<br />
<b>¿Cómo ponerlo si ese símbolo es un carácter especial de la SQL que representa el comodín?</b>. Rebuscando por internet, he encontrado la solución para buscar este carácter especial:<br />
<br />
<code><span class="Apple-style-span" style="color: blue;">SELECT</span> * <span class="Apple-style-span" style="color: blue;">FROM</span> <span class="Apple-style-span" style="color: red;">MI_TABLA</span><br />
<span class="Apple-style-span" style="color: blue;">WHERE</span> DESCRIPCION <span class="Apple-style-span" style="color: blue;">LIKE</span> <span class="Apple-style-span" style="color: purple;">'%!%%'</span> <span class="Apple-style-span" style="color: blue;">ESCAPE </span><span class="Apple-style-span" style="color: purple;">'!'</span><span class="Apple-style-span" style="color: blue;">;</span></code>
<br />
Usando la clausula de <span class="Apple-style-span" style="color: blue;">ESCAPE</span> puedes buscar caracteres que normalmente no los podrías buscar. Se pueden buscar cadenas de caracteres que incluyan uno o más caracteres comodín especiales. Para buscar el signo de porcentaje como un carácter, en lugar de buscarlo como un carácter comodín, hay que proporcionar la palabra clave <span class="Apple-style-span" style="color: blue;">ESCAPE</span> y el carácter que queremos <i>"ignorar"</i>. Si no pusieramos ese símbolo de exclamación y el <span class="Apple-style-span" style="color: blue;">ESCAPE </span><span class="Apple-style-span" style="color: purple;">'!'</span>, devolvería cualquier fila.<br />
<br />
He puesto el símbolo de exclamación por poner un ejemplo, pero funcionaría exactamente igual si pusieramos cualquier otro caráctero. La cláusula <span class="Apple-style-span" style="color: blue;">ESCAPE</span> ignorará ese carácter del LIKE y permitirá buscar el resto. <br />
<br />
<code><span class="Apple-style-span" style="color: blue;">WHERE</span> DESCRIPCION <span class="Apple-style-span" style="color: blue;">LIKE</span> <span class="Apple-style-span" style="color: purple;">'%z%%'</span> <span class="Apple-style-span" style="color: blue;">ESCAPE </span><span class="Apple-style-span" style="color: purple;">'z'</span><span class="Apple-style-span" style="color: blue;">;</span></code>
<br />
Espero que os sirva como me ha servido a mi ;). Un saludo.ElPaterhttp://www.blogger.com/profile/16022680446046453947noreply@blogger.com5tag:blogger.com,1999:blog-162184249783410837.post-48555502350821156162011-08-03T16:09:00.002+02:002012-04-04T13:40:13.859+02:00Matar proceso en Oracle (y PowerBuilder)<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjOwJlb-cVVMyLsDaiq2uoDOHIT-_efLe7Qkz2bNU6IlooDIYbGND7hSCWUCefSjtMN0xOzkDP0KXfhQqyQjVmgFhGZKXm-nyweCO5Ed9x8YltNom-X9_GUU7VzAOlV3aYdYtqIZZp8ZsM/s1600/gnome-panel-force-quit.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjOwJlb-cVVMyLsDaiq2uoDOHIT-_efLe7Qkz2bNU6IlooDIYbGND7hSCWUCefSjtMN0xOzkDP0KXfhQqyQjVmgFhGZKXm-nyweCO5Ed9x8YltNom-X9_GUU7VzAOlV3aYdYtqIZZp8ZsM/s1600/gnome-panel-force-quit.png" /></a></div>
Siguiendo el <a href="http://picarpiezas.blogspot.com/2011/08/select-para-ver-bloqueos.html"><b>[artículo anterior]</b></a> (en el que construíamos una <b><span class="Apple-style-span" style="color: #38761d;">Select </span></b>para consultar los procesos que estaban bloqueando tablas o filas en nuestra Base de Datos) ahora veremos cómo se pueden terminar (o matar) estos procesos por SQL. Como en casos anteriores, esto funciona en <b><span class="Apple-style-span" style="color: #38761d;">Oracle</span></b> , y hay que tener <b>privilegios de <span class="Apple-style-span" style="color: #38761d;">DBA</span></b>.<br />
<br />
La sentencia es bastante simple:<br />
<code><span class="Apple-style-span" style="color: blue;">ALTER SYSTEM KILL SESSION</span> '<i>SID</i>,<i>SERIAL#</i>'<span class="Apple-style-span" style="color: blue;">;</span></code><br />
Es decir, las dos últimas columnas que recuperamos en la <b><span class="Apple-style-span" style="color: #38761d;">Select </span></b>de los bloqueos (ver <b><a href="http://picarpiezas.blogspot.com/2011/08/select-para-ver-bloqueos.html"><artículo anterior></a>) </b>son las que utilizaremos para identificar el proceso y acabarlo. La sentencia del <span class="Apple-style-span" style="color: #38761d;"><b>KILL SESSION</b></span> queda del tipo<br />
<code><span class="Apple-style-span" style="color: blue;">ALTER SYSTEM KILL SESSION</span> '<i>1587</i>,<i>58656</i>'<span class="Apple-style-span" style="color: blue;">;</span></code><br />
<br />
<b>¿Y como ejecutar esto en un botón de PowerBuilder?</b><br />
<br />
Se coge el SID y #Serial del proceso a matar y se construye una cadena que ejecutaremos con un <b><span class="Apple-style-span" style="color: #38761d;">EXECUTE INMEDIATE</span></b>. Ya que tenemos la <span class="Apple-style-span" style="color: #38761d;">DataWindow </span>con la consulta hecha, podemos usarla para que coja el SID y #Serial de la fila que seleccione el usuario.<br />
Este es el código de mi botón.<br />
<br />
<code><span class="Apple-style-span" style="color: purple;">Long<span class="Apple-style-span" style="white-space: pre;"> </span></span>lFila<br />
<span class="Apple-style-span" style="color: purple;">String<span class="Apple-style-span" style="white-space: pre;"> </span></span>sSID, sSerial, sUser, sObjeto, sKILL<br />
lFila = dw_1.GetSelectedRow(0)<br />
<br />
<span class="Apple-style-span" style="color: #38761d;"> IF</span> lFila = 0 <span class="Apple-style-span" style="color: #38761d;">THEN</span><br />
<span class="Apple-style-span" style="white-space: pre;"> </span>MessageBox(<span class="Apple-style-span" style="color: #38761d;">Parent</span>.Title, <span class="Apple-style-span" style="color: #990000;">"Seleccione un proceso."</span>)<br />
<span class="Apple-style-span" style="white-space: pre;"> </span><span class="Apple-style-span" style="color: #38761d;">Return</span><br />
<span class="Apple-style-span" style="color: #38761d;"> END IF</span><br />
<span class="Apple-style-span" style="color: purple;"></span> <br />
sSID = dw_1.GetItemString(lFila, <span class="Apple-style-span" style="color: #990000;">"sid"</span>)<br />
sSerial = dw_1.GetItemString(lFila, <span class="Apple-style-span" style="color: #990000;">"serial"</span>)<br />
sUser = dw_1.GetItemString(lFila, <span class="Apple-style-span" style="color: #990000;">"usuario_sist_operat"</span>)<br />
sObjeto = dw_1.GetItemString(lFila, <span class="Apple-style-span" style="color: #990000;">"dba_objects_objeto_bloqueado "</span>)<br />
sKILL = <span class="Apple-style-span" style="color: #990000;">" ALTER SYSTEM KILL SESSION '"</span> + sSID + <span class="Apple-style-span" style="color: #990000;">","</span> + sSerial + <span class="Apple-style-span" style="color: #990000;">"'; "</span><br />
<span class="Apple-tab-span" style="white-space: pre;"> </span><br />
<span class="Apple-style-span" style="color: #38761d;"> IF</span> MessageBox(<span class="Apple-style-span" style="color: #38761d;">Parent</span>.Title, <span class="Apple-style-span" style="color: #990000;">"¿Desea matar el proceso '"</span> + sSID + <span class="Apple-style-span" style="color: #990000;">","</span> + sSerial + <span class="Apple-style-span" style="color: #990000;">"' de "</span> + sUser + <span class="Apple-style-span" style="color: #990000;">" sobre el objeto "</span> + sObjeto + <span class="Apple-style-span" style="color: #990000;">"?"</span>,<span class="Apple-style-span" style="color: #0b5394;">Exclamation!</span>,<span class="Apple-style-span" style="color: #0b5394;">YesNo!</span>,2) = 1 <span class="Apple-style-span" style="color: #38761d;">THEN</span><br />
<span class="Apple-style-span" style="white-space: pre;"></span> <span class="Apple-style-span" style="white-space: pre;"> </span><span class="Apple-style-span" style="color: #38761d;">EXECUTE IMMEDIATE</span> :sKILL ; <span class="Apple-style-span" style="color: blue;">// Se ejecuta la cadena.</span><br />
<span class="Apple-style-span" style="color: #38761d;"> END IF</span><br />
<br />
dw_1.Retrieve()</code><br />
<br />
Y ya está. <b>:)</b><br />
<br />
Cuando se quiera matar un proceso, se selecciona la fila, y se da al botón. Aparece un mensaje de confirmación, y el proceso bloqueado desaparecerá.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgMXckfJvX5H6TIpJVnvLNHvZJ7JnNEEqjEDargHmIHxETw0wO1gfbueRWHTByLtZBwqpJgzVakIpcp-ZtOnnzKlyE9oWjTzyknM0fAvY6OmzlY3iklhp-zoNKK9LmVcnGDP8fGjDVPV_M/s1600/bloqueos.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="190" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgMXckfJvX5H6TIpJVnvLNHvZJ7JnNEEqjEDargHmIHxETw0wO1gfbueRWHTByLtZBwqpJgzVakIpcp-ZtOnnzKlyE9oWjTzyknM0fAvY6OmzlY3iklhp-zoNKK9LmVcnGDP8fGjDVPV_M/s640/bloqueos.jpg" width="600" /></a></div>
<br />
Hay que informar que todas estas operaciones tienen su <b>peligro</b>. Su uso debe ser exclusivo para alguien que sepa lo que está haciendo.<br />
Un proceso bloqueado no es un mal proceso: puede que deba bloquear una tabla o registro un determinado tiempo mientras realiza una operación. Que el proceso esté bloqueado mucho (muchísimo) tiempo puede ser síntoma de que algo va mal, pero esta herramienta que hemos creado es para casos extremos en que queramos matar un proceso que se ha quedado perdido en el limbo de la Base de Datos. No debe usarse a discreción, porque podemos matar procesos realmente "<i>vivos</i>".<br />
<br />
Usar con cuidado. ;)ElPaterhttp://www.blogger.com/profile/16022680446046453947noreply@blogger.com0tag:blogger.com,1999:blog-162184249783410837.post-64612159017638511022011-08-01T16:13:00.009+02:002011-10-31T10:36:45.339+01:00SELECT para ver bloqueos<div class="separator" style="clear: both; text-align: center;"></div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj0GwDGv5eZIW2LANJaDL2nAkOa__dVdB6s6w9LbgsZR9RSjRCPYsAjQo7fR3_qEylnTsnZU42ceHZIyZOATWClZianCqECI_-Nl9siaZRdy0nEPJUyD60XYwpEM-IhJGnIvj7opQZxF7A/s1600/Gnome-System-Lock-Screen-64.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj0GwDGv5eZIW2LANJaDL2nAkOa__dVdB6s6w9LbgsZR9RSjRCPYsAjQo7fR3_qEylnTsnZU42ceHZIyZOATWClZianCqECI_-Nl9siaZRdy0nEPJUyD60XYwpEM-IhJGnIvj7opQZxF7A/s1600/Gnome-System-Lock-Screen-64.png" /></a></div>¿Alguna vez has necesitado saber si algún proceso está bloqueando alguna de tus tablas?<br />
A continuación una <b><span class="Apple-style-span" style="color: #38761d;">Select para Oracle</span></b> que permite consultar los procesos que estan produciendo bloqueos en la Base de Datos. IMPORTANTE: Sólo funcionará si se ejecuta con <b>privilegios de <span class="Apple-style-span" style="color: #38761d;">DBA</span></b>, ya que consulta tablas del sistema de la BD.<br />
<br />
<code><span class="Apple-style-span" style="color: blue;">SELECT</span> <span class="Apple-style-span" style="color: red;">decode</span>(L.TYPE,<span class="Apple-style-span" style="color: purple;">'TM'</span>,<span class="Apple-style-span" style="color: purple;">'TABLE'</span>,<span class="Apple-style-span" style="color: purple;">'TX'</span>,<span class="Apple-style-span" style="color: purple;">'Record(s)'</span>) TIPO_BLOQUEO,<br />
<span class="Apple-style-span" style="color: red;">decode</span>(L.REQUEST,0,<span class="Apple-style-span" style="color: purple;">'NO'</span>,<span class="Apple-style-span" style="color: purple;">'YES'</span>) ESPERA,<br />
S.SECONDS_IN_WAIT SEGUNDOS_EN_ESPERA,<br />
<span class="Apple-style-span" style="color: red;">decode</span>(l.LMODE,0,<span class="Apple-style-span" style="color: purple;">'none'</span>,1,<span class="Apple-style-span" style="color: purple;">'null (NULL)'</span>,2,<span class="Apple-style-span" style="color: purple;">'row-S (SS)'</span>,3,<span class="Apple-style-span" style="color: purple;">'row-X (SX)'</span>,4,<span class="Apple-style-span" style="color: purple;">'share (S)'</span>,5,<span class="Apple-style-span" style="color: purple;">'S/Row-X (SSX)'</span>,6,<span class="Apple-style-span" style="color: purple;">'exclusive (X)'</span>) MODO_BLOQUEO,<br />
S.OSUSER USUARIO_SIST_OPERAT,<br />
S.USERNAME USUARIO_BD,<br />
S.PROCESS PROCESS_LOCKER,<br />
S.MACHINE MAQUINA,<br />
O.OBJECT_NAME OBJETO_BLOQUEADO,<br />
O.OBJECT_TYPE OBJETO_TIPO,<br />
<span class="Apple-style-span" style="color: red;">concat</span>(<span class="Apple-style-span" style="color: purple;">' '</span>,s.PROGRAM) PROGRAMA,<br />
O.OWNER PROPIETARIO,<br />
S.SID,<br />
S.SERIAL#<br />
<span class="Apple-style-span" style="color: blue;">FROM</span> <span class="Apple-style-span" style="color: red;">v$lock</span> L,<br />
<span class="Apple-style-span" style="color: red;">dba_objects</span> O,<br />
<span class="Apple-style-span" style="color: red;">v$session</span> S<br />
<span class="Apple-style-span" style="color: blue;">WHERE</span> L.ID1 <span class="Apple-style-span" style="color: blue;">=</span> O.OBJECT_ID<br />
<span class="Apple-style-span" style="color: blue;">AND</span> S.SID <span class="Apple-style-span" style="color: blue;">=</span> L.SID<br />
<span class="Apple-style-span" style="color: blue;">AND</span> L.TYPE <span class="Apple-style-span" style="color: blue;">in</span> (<span class="Apple-style-span" style="color: purple;">'TM'</span>,<span class="Apple-style-span" style="color: purple;">'TX'</span>) <span class="Apple-style-span" style="color: blue;">;</span></code><br />
<br />
Se utilizan las tablas <a href="http://download.oracle.com/docs/cd/B19306_01/server.102/b14237/statviews_2005.htm"><b><span class="Apple-style-span" style="color: red; font-family: 'Courier New', Courier, monospace;">dba_objects</span></b></a> (para algún dato del objeto bloqueado), <a href="http://download.oracle.com/docs/cd/B19306_01/server.102/b14237/dynviews_1147.htm"><span class="Apple-style-span" style="color: red; font-family: 'Courier New', Courier, monospace;"><b>v$lock</b></span></a> y <a href="http://download.oracle.com/docs/cd/B19306_01/server.102/b14237/dynviews_2088.htm"><span class="Apple-style-span" style="color: red; font-family: 'Courier New', Courier, monospace;"><b>v$session</b></span></a> (que son tablas de Oracle que contienen datos de las sesiones activas en cada momento y los bloqueos).<br />
<br />
En mi caso, y como ejemplo, creé una ventana con una DataWindow con esta Select, que se refresca cada 5 segundos. Más tarde lo cambié a 30, y más tarde puse una lista para que cada uno elija el tiempo que quiera. En una Base de Datos muy utilizada como es la de mi trabajo, se veían los bloqueos apareciendo y desapareciendo de una forma muy elegante.<br />
<br />
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgMXckfJvX5H6TIpJVnvLNHvZJ7JnNEEqjEDargHmIHxETw0wO1gfbueRWHTByLtZBwqpJgzVakIpcp-ZtOnnzKlyE9oWjTzyknM0fAvY6OmzlY3iklhp-zoNKK9LmVcnGDP8fGjDVPV_M/s1600/bloqueos.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="190" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgMXckfJvX5H6TIpJVnvLNHvZJ7JnNEEqjEDargHmIHxETw0wO1gfbueRWHTByLtZBwqpJgzVakIpcp-ZtOnnzKlyE9oWjTzyknM0fAvY6OmzlY3iklhp-zoNKK9LmVcnGDP8fGjDVPV_M/s640/bloqueos.jpg" width="600" /></a></div><br />
La columna de "SEGUNDOS_EN_ESPERA" se puede formatear con una función del tipo "RelativeTime('00:00:00',NSegundos)" para mostrarlo de forma más clara.<br />
<br />
Las columnas SID y #Serial (que a priori no nos dicen nada) se utilizan para construir el botón "<b><i>Matar</i></b>", ya que necesité cortar algunas de las sesiones que estaba viendo porque los bloqueos eran constantes, eternos y provocados por un funcionamiento erróneo de la aplicación.<br />
En el <a href="http://picarpiezas.blogspot.com/2011/08/matar-proceso-en-oracle-y-powerbuilder.html"><b>[siguiente artículo]</b></a> pondré el código de este botón que mata una de las sesiones vistas en esta Select.ElPaterhttp://www.blogger.com/profile/16022680446046453947noreply@blogger.com3tag:blogger.com,1999:blog-162184249783410837.post-27965383706699998482011-04-14T13:20:00.004+02:002011-08-03T16:41:05.691+02:00Datawindows con Treeviews dinámicos en PowerBuilder<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiurr7uGQvo4Tkth0IcW-L806Z_u_7ioN2J1G4jdAzgW_No3ZPOOEWAwHurm9yKnx1Vu4LRPTgvJVij7SZsEDRJ7pXpwbz-l_pBQMdrwdwF7J0RErILDA0mYGPN2b_21biHlX264_jKDMA/s1600/content-tree.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiurr7uGQvo4Tkth0IcW-L806Z_u_7ioN2J1G4jdAzgW_No3ZPOOEWAwHurm9yKnx1Vu4LRPTgvJVij7SZsEDRJ7pXpwbz-l_pBQMdrwdwF7J0RErILDA0mYGPN2b_21biHlX264_jKDMA/s1600/content-tree.png" /></a></div>A veces tenemos una <span class="Apple-style-span" style="color: #38761d;"><b>DataWindow</b></span> en forma de <b><span class="Apple-style-span" style="color: #38761d;">TreeView</span></b>, y nos gustaría que el usuario pudiera elegir a su antojo el orden de la agrupación. Si tenemos una DataWindow de 4 grupos, se podría hacer una ristra de 24 DataWindows para que el usuario, elija lo que elija, se lo pintemos en la DataWindow agrupado. Pero esto es una locura.<br />
<br />
Os voy a enseñar cómo se puede hacer que con una sola DataWindow se pueda cambiar dinámicamente la agrupación. Acompáñame tras el salto.<br />
<a name='more'></a><br />
Lo primero que tenemos que hacer es crear la DataWindow con los grupos, y crear 4 <b><span class="Apple-style-span" style="color: #38761d;">computes</span></b> (el ejemplo lo voy a hacer con 4, pero se pueden poner 2, 3, 5 o los que se necesiten. Hay que hacer tantos computes como grupos queramos). A cada compute lo he llamado <i>campo_1</i>, <i>campo_2</i>, etc.<br />
<br />
La agrupación de cada grupo irá por estos computes. Es decir, el grupo 1 agrupado por campo_1 y así sucesivamente:<br />
<div style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj8sbxM7KmBO8vm6LS8c0rD5KFZ-8Ww5PjL3xy17yfZlqMH4yv-YdLhwiRTw-NZVpR5VARHzChM3KdzECmXRpPpYmZhXvri0pGLiLyN3iRRmM3vYj-tmeVFw8AbffIZZqOuWx_70HckgJ0/s1600/TreeView1.gif" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="200" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj8sbxM7KmBO8vm6LS8c0rD5KFZ-8Ww5PjL3xy17yfZlqMH4yv-YdLhwiRTw-NZVpR5VARHzChM3KdzECmXRpPpYmZhXvri0pGLiLyN3iRRmM3vYj-tmeVFw8AbffIZZqOuWx_70HckgJ0/s400/TreeView1.gif" width="400" /></a></div>Ahora que tenemos la DataWindow, hay que dar al usuario la forma de que elija su agrupación personalizada. Yo lo he hecho insertando un simple <b><span class="Apple-style-span" style="color: #38761d;">ListBox </span></b>en la ventana, en la que el usuario va añadiendo su ordenación. Puede elegir 1, 2, 3 o 4 elementos para agrupar:<br />
<div style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi7FYikaIbSb5aMIif7hUHhoI1lzyLirJEElv9fs8Mu0hGLCRrKvvCZZrbg3C4eYtx6IsRhBRCMWPynvVSgieDnTLogllvBNl6tUkfrTdk5E4DCkIdJr4Rlet27D7ame-sUrcOoeTad2m4/s1600/TreeView2.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="138" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi7FYikaIbSb5aMIif7hUHhoI1lzyLirJEElv9fs8Mu0hGLCRrKvvCZZrbg3C4eYtx6IsRhBRCMWPynvVSgieDnTLogllvBNl6tUkfrTdk5E4DCkIdJr4Rlet27D7ame-sUrcOoeTad2m4/s400/TreeView2.jpg" width="400" /></a></div>Cuando ya tenemos la selección escogida, lo único que hay que hacer es montar y configurar el DataWindow para que agrupe dinámicamente. Hay que hacer una función que coja este orden, y ponga la columna que corresponde en cada compute de los insertados. Yo me he creado una función en la ventana en la que le paso 2 strings por referencia. Uno para decirle qué "grupo" ha seleccionado, y devolverá en esa variable la columna de la DataWindow por la que debe agrupar, y el orden por el que se basa esa columna.<br />
<br />
<code>// <b>wfSetNivelYOrden</b>(ref string srNivel, ref string srOrden)<br />
<span class="Apple-style-span" style="color: purple;"> String </span>sCampoGerencia, sCampoDependencia, sCampoTipoAccion, sCampoAccion, sSort<br />
<br />
sCampoGerencia = <span class="Apple-style-span" style="color: #990000;">"adn"</span><br />
sCampoDependencia = <span class="Apple-style-span" style="color: #990000;">"dependencia"</span><br />
sCampoTipoAccion = <span class="Apple-style-span" style="color: #990000;">"descripcion_tipoaccion"</span><br />
sCampoAccion = <span class="Apple-style-span" style="color: #990000;">"descripcion_accion"</span><br />
<br />
<span class="Apple-style-span" style="color: #38761d;"> CHOOSE CASE</span> srNivel<br />
<span class="Apple-style-span" style="color: #38761d;">CASE </span><span class="Apple-style-span" style="color: #cc0000;">"Gerencia"</span><br />
srNivel = sCampoGerencia<br />
<span class="Apple-style-span" style="color: #38761d;">CASE </span><span class="Apple-style-span" style="color: #990000;">"Dependencia"</span><br />
srNivel = sCampoDependencia<br />
<span class="Apple-style-span" style="color: #38761d;">CASE </span><span class="Apple-style-span" style="color: #990000;">"Tipo de acción"</span><br />
srNivel = sCampoTipoAccion<br />
<span class="Apple-style-span" style="color: #38761d;">CASE </span><span class="Apple-style-span" style="color: #990000;">"Acción"</span><br />
srNivel = sCampoAccion<br />
srOrden = <span class="Apple-style-span" style="color: #990000;">"acciones_id A"</span><br />
<span class="Apple-style-span" style="color: #38761d;">CASE ELSE</span><br />
srNivel = <span class="Apple-style-span" style="color: #990000;">""</span><br />
srOrden = <span class="Apple-style-span" style="color: #990000;">""</span><br />
<span class="Apple-style-span" style="color: #38761d;">Return</span><br />
<span class="Apple-style-span" style="color: #38761d;"> END CHOOSE</span><br />
<br />
<span class="Apple-style-span" style="color: #38761d;"> IF </span>srOrden = <span class="Apple-style-span" style="color: #990000;">""</span> <span class="Apple-style-span" style="color: #38761d;">THEN </span>srOrden = srNivel + <span class="Apple-style-span" style="color: #990000;">" A"</span><br />
<span class="Apple-style-span" style="color: #38761d;"> Return</span></code><br />
Además, hay que <b>expandir/contraer</b> cada grupo para que todo quede correcto. Un ejemplo, es la función siguiente:<br />
<code><br />
<span class="Apple-style-span" style="color: purple;"> String </span> sNivel1, sNivel2, sNivel3, sNivel4<br />
<span class="Apple-style-span" style="color: purple;"> String </span> sOrden1, sOrden2, sOrden3, sOrden4<br />
<span class="Apple-style-span" style="color: purple;"> Integer </span> iNiveles<br />
<span class="Apple-style-span" style="color: purple;"> String </span> sSort<br />
dw_datos.SetRedraw(<span class="Apple-style-span" style="color: #38761d;">FALSE</span>)<br />
<br />
<span class="Apple-style-span" style="color: blue;"> // Datawindow de árbol</span><br />
sNivel1 = lb_1.text(1)<br />
sNivel2 = lb_1.text(2)<br />
sNivel3 = lb_1.text(3)<br />
sNivel4 = lb_1.text(4)<br />
iNiveles = lb_1.TotalItems()<br />
<br />
<span class="Apple-style-span" style="color: #38761d;"> IF </span>iNiveles = 0 <span class="Apple-style-span" style="color: #38761d;">THEN</span><br />
sNivel1 = <span class="Apple-style-span" style="color: #990000;">"Gerencia"</span><br />
sNivel2 = <span class="Apple-style-span" style="color: #990000;">"Dependencia"</span><br />
sNivel3 = <span class="Apple-style-span" style="color: #990000;">"Tipo de Acción"</span><br />
sNivel4 = <span class="Apple-style-span" style="color: #990000;">"Acción"</span><br />
<span class="Apple-style-span" style="color: #38761d;"> END IF</span><br />
<br />
<span class="Apple-style-span" style="color: blue;"> // Coge qué columna y cómo se ordenará cada nivel.</span><br />
wfSetNivelYOrden(sNivel1,sOrden1)<br />
wfSetNivelYOrden(sNivel2,sOrden2)<br />
wfSetNivelYOrden(sNivel3,sOrden3)<br />
wfSetNivelYOrden(sNivel4,sOrden4)<br />
<br />
<span class="Apple-style-span" style="color: blue;"> // Se reorganiza el TreeView con la configuración.</span><br />
dw_datos.modify("campo_1.expression='" + sNivel1 + "'")<br />
dw_datos.modify("campo_2.expression='" + sNivel2 + "'")<br />
dw_datos.modify("campo_3.expression='" + sNivel3 + "'")<br />
dw_datos.modify("campo_4.expression='" + sNivel4 + "'")<br />
sSort = sOrden1 + ", " + sOrden2 + ", " + sOrden3 + ", " + sOrden4 + ", ps_acciones_mes_id A"<br />
<span class="Apple-style-span" style="color: blue;"> // Se ocultan los niveles que no se usarán. (1, 2 o 3)</span><br />
<span class="Apple-style-span" style="color: #38761d;"> CHOOSE CASE</span> iNiveles<br />
<span class="Apple-style-span" style="color: #38761d;">CASE </span>1<br />
dw_datos.Modify("DataWindow.tree.level.2.Height=0")<br />
dw_datos.Modify("DataWindow.tree.level.3.Height=0")<br />
dw_datos.Modify("DataWindow.tree.level.4.Height=0")<br />
dw_datos.Modify("DataWindow.tree.level.2.Height.AutoSize='false'")<br />
dw_datos.Modify("DataWindow.tree.level.3.Height.AutoSize='false'")<br />
dw_datos.Modify("DataWindow.tree.level.4.Height.AutoSize='false'")<br />
dw_datos.Modify("DataWindow.Detail.Height=0")<br />
<span class="Apple-style-span" style="color: #38761d;">CASE </span>2<br />
dw_datos.Modify("DataWindow.tree.level.2.Height=68")<br />
dw_datos.Modify("DataWindow.tree.level.3.Height=0")<br />
dw_datos.Modify("DataWindow.tree.level.4.Height=0")<br />
dw_datos.Modify("DataWindow.tree.level.2.Height.AutoSize='true'")<br />
dw_datos.Modify("DataWindow.tree.level.3.Height.AutoSize='false'")<br />
dw_datos.Modify("DataWindow.tree.level.4.Height.AutoSize='false'")<br />
dw_datos.Modify("DataWindow.Detail.Height=0")<br />
<span class="Apple-style-span" style="color: #38761d;">CASE </span>3<br />
dw_datos.Modify("DataWindow.tree.level.2.Height=68")<br />
dw_datos.Modify("DataWindow.tree.level.3.Height=68")<br />
dw_datos.Modify("DataWindow.tree.level.4.Height=0")<br />
dw_datos.Modify("DataWindow.tree.level.2.Height.AutoSize='true'")<br />
dw_datos.Modify("DataWindow.tree.level.3.Height.AutoSize='true'")<br />
dw_datos.Modify("DataWindow.tree.level.4.Height.AutoSize='false'")<br />
dw_datos.Modify("DataWindow.Detail.Height=0")<br />
<span class="Apple-style-span" style="color: #38761d;">CASE </span>4, 0<br />
dw_datos.Modify("DataWindow.tree.level.2.Height=68")<br />
dw_datos.Modify("DataWindow.tree.level.3.Height=68")<br />
dw_datos.Modify("DataWindow.tree.level.4.Height=68")<br />
dw_datos.Modify("DataWindow.tree.level.2.Height.AutoSize='true'")<br />
dw_datos.Modify("DataWindow.tree.level.3.Height.AutoSize='true'")<br />
dw_datos.Modify("DataWindow.tree.level.4.Height.AutoSize='true'")<br />
dw_datos.Modify("DataWindow.Detail.Height=68")<br />
<span class="Apple-style-span" style="color: #38761d;"> END CHOOSE</span><br />
<br />
sSort = Left(sSort,Len(sSort)-1)<br />
dw_datos.SetSort(sSort)<br />
dw_datos.Sort()<br />
dw_datos.GroupCalc()<br />
dw_datos.ExpandAll()<br />
dw_datos.SetRedraw(<span class="Apple-style-span" style="color: #38761d;">TRUE</span>)<br />
<br />
<span class="Apple-style-span" style="color: blue;"> // Todo bien.</span><br />
<span class="Apple-style-span" style="color: #38761d;"> Return</span></code><br />
Nótese, que da igual cuándo hagamos el <span class="Apple-style-span" style="color: #38761d;"><b>Retrieve()</b></span> de la DataWindow. Esta agrupación funciona tanto si los datos están ya en la DataWindow como si los vamos a traer a continuación. A mi me gusta que los datos ya estén en la DataWindow, para que esta agrupación se vea más dinámica y directa (ya que la agrupación tarda sólo unas milésimas de segundo en realizarse a los ojos del usuario).<br />
<br />
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhQgUYaLyEwR89x9HHLkRDBEAgx_eOT6K-LmeISB1aEX0LgCYE6HXc_y0YV_kGSAW1Qq8_uAD0P55Cnv2lKWJZFQEMaSR2CTpipiHl544yHr5pcvKG1YQzCeqJw-V23ooTc4J75G6SosTk/s1600/TreeView3.gif" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="235" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhQgUYaLyEwR89x9HHLkRDBEAgx_eOT6K-LmeISB1aEX0LgCYE6HXc_y0YV_kGSAW1Qq8_uAD0P55Cnv2lKWJZFQEMaSR2CTpipiHl544yHr5pcvKG1YQzCeqJw-V23ooTc4J75G6SosTk/s400/TreeView3.gif" width="400" /></a></div><br />
Espero que les sirva de ayuda. Para cualquier duda o sugerencia, los <b>comentarios</b> están abiertos ;)ElPaterhttp://www.blogger.com/profile/16022680446046453947noreply@blogger.com4tag:blogger.com,1999:blog-162184249783410837.post-85640371171576546742011-04-06T12:24:00.006+02:002013-11-25T09:58:11.599+01:00Como ejecutar cualquier fichero en PowerBuilder<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhFiVEjs_PZjk1rC4gZlaSZDh66dZoVgQFbcGpyw_zR_VbLCXY6civWCsiFWEKPq7oxacOOkt-x6JsXVry6JlHgQ90NSF_GTr8_AQ9Ic8vNZtt6m2xvLdxy1p1HIyNMfsukR-jVuE8ts0k/s1600/run-build.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhFiVEjs_PZjk1rC4gZlaSZDh66dZoVgQFbcGpyw_zR_VbLCXY6civWCsiFWEKPq7oxacOOkt-x6JsXVry6JlHgQ90NSF_GTr8_AQ9Ic8vNZtt6m2xvLdxy1p1HIyNMfsukR-jVuE8ts0k/s1600/run-build.png" /></a></div>Para ejecutar un fichero en PowerBuilder se puede utilizar la función <b>Run()</b>, pero es un método bastante básico que sólo permite ejecutar .exe.<br />
<br />
Para poder ejecutar cualquier fichero, incluso si es una hoja de Excel, un DOC, un MP3, un AVI, etc... como si le dieses doble-click en el explorador, hay que utilizar una llamada al API de Windows <b><span class="Apple-style-span" style="color: #38761d;">ShellExecute</span></b>. Veamos cómo utilizarlo.<br />
<br />
<a name='more'></a><br />
Lo primero es declarar una Función Global que tenga la siguiente definición.<br />
<code>FUNCTION long ShellExecute(ulong ihwnd,string lpszOp,string lpszFile,string lpszParams,&<br />
string lpszDir,int wShowCmd ) LIBRARY "shell32.dll" ALIAS FOR "ShellExecuteW"</code><br />
Después hay que ejecutar el siguiente código. Lo mejor es que nos creemos una <b>Función Global</b> con la siguiente estructura, y así poder llamarla desde donde queramos.<br />
<code><span class="Apple-style-span" style="color: blue;">//-------------------------------------------------------------------------<br />
// Función : long gf_Ejecutar(String spRuta)<br />
// Propósito : Ejecuta un fichero, sea lo que sea (doc, pdf, xls, mp3...)<br />
// Parámetros : spRuta.- Ruta del fichero a ejecutar.<br />
// Devuelve : 1.- Todo bien<br />
// -1.- Hay error.<br />
//-------------------------------------------------------------------------</span><br />
<span class="Apple-style-span" style="color: purple;"> Long </span>lRetorno<br />
<span class="Apple-style-span" style="color: purple;"> String </span>sError, sNulo<br />
<br />
SetNull(sNulo)
<br />
<span class="Apple-style-span" style="color: blue;"> // Se ejecuta la ruta pasada.</span><br />
lRetorno = ShellExecute(Handle(this), sNulo, spRuta, sNulo, sNulo, 3)<br />
<span class="Apple-style-span" style="color: blue;"> //El último argumento es el estilo de la ventana:<br />
//SW_HIDE 0<br />
//SW_SHOWNORMAL 1<br />
//SW_NORMAL 1<br />
//SW_SHOWMINIMIZED 2<br />
//SW_SHOWMAXIMIZED 3<br />
//SW_MAXIMIZE 3<br />
//SW_SHOWNOACTIVATE 4<br />
//SW_SHOW 5<br />
//SW_MINIMIZE 6<br />
//SW_SHOWMINNOACTIVATE 7<br />
//SW_SHOWNA 8<br />
//SW_RESTORE 9<br />
//SW_SHOWDEFAULT 10<br />
//SW_MAX 10</span><br />
<br />
<span class="Apple-style-span" style="color: #38761d;"> IF </span>lRetorno <= 32 <span class="Apple-style-span" style="color: #38761d;">THEN</span><br />
<span class="Apple-style-span" style="color: blue;">// Hay error</span><br />
<span class="Apple-style-span" style="color: #38761d;">CHOOSE CASE</span> lRetorno<br />
<span class="Apple-style-span" style="color: #38761d;">CASE</span> 2<br />
sError = <span class="Apple-style-span" style="color: #990000;">"Fichero no encontrado"</span><br />
<span class="Apple-style-span" style="color: #38761d;">CASE </span>3<br />
sError = <span class="Apple-style-span" style="color: #990000;">"Ruta no encontrada"</span><br />
<span class="Apple-style-span" style="color: #38761d;">CASE </span>5<br />
sError = <span class="Apple-style-span" style="color: #990000;">"Acceso denegado"</span><br />
<span class="Apple-style-span" style="color: #38761d;">CASE </span>8<br />
sError = <span class="Apple-style-span" style="color: #990000;">"Fuera de memoria"</span><br />
<span class="Apple-style-span" style="color: #38761d;">CASE </span>32<br />
sError = <span class="Apple-style-span" style="color: #990000;">"DLL no encontrada"</span><br />
<span class="Apple-style-span" style="color: #38761d;">CASE </span>26<br />
sError = <span class="Apple-style-span" style="color: #990000;">"A sharing violation occurred"</span><br />
<span class="Apple-style-span" style="color: #38761d;">CASE </span>27<br />
sError = <span class="Apple-style-span" style="color: #990000;">"Asociación a fichero no válida o incompleta"</span><br />
<span class="Apple-style-span" style="color: #38761d;">CASE </span>28<br />
sError = <span class="Apple-style-span" style="color: #990000;">"DDE Time out"</span><br />
<span class="Apple-style-span" style="color: #38761d;">CASE </span>29<br />
sError = <span class="Apple-style-span" style="color: #990000;">"DDE transaction failed"</span><br />
<span class="Apple-style-span" style="color: #38761d;">CASE</span> 30<br />
sError = <span class="Apple-style-span" style="color: #990000;">"DDE busy"</span><br />
<span class="Apple-style-span" style="color: #38761d;">CASE </span>31<br />
sError = <span class="Apple-style-span" style="color: #990000;">"No existe ninguna asociación a la extensión del fichero"</span><br />
<span class="Apple-style-span" style="color: #38761d;">CASE </span>11<br />
sError = <span class="Apple-style-span" style="color: #990000;">"Invalid EXE file or error in EXE image"</span><br />
<span class="Apple-style-span" style="color: #38761d;">CASE ELSE</span><br />
sError = <span class="Apple-style-span" style="color: #990000;">"Error desconocido"</span><br />
<span class="Apple-style-span" style="color: #38761d;">END CHOOSE</span><br />
Messagebox(<span class="Apple-style-span" style="color: #990000;">"Error..."</span> + <span class="Apple-style-span" style="color: purple;">String</span>(lRetorno),sError)<br />
<span class="Apple-style-span" style="color: #38761d;">Return</span> -1<br />
<span class="Apple-style-span" style="color: #38761d;"> END IF</span><br />
<br />
<span class="Apple-style-span" style="color: blue;"> // Todo bien.</span><br />
<span class="Apple-style-span" style="color: #38761d;"> Return</span> 1</code><br />
Teniendo esta función global, tan sólo tenemos que llamarla desde cualquier código, pasándole la ruta. Por ejemplo, desde un botón.<br />
<code>gf_Ejecutar("C:\Mis Documentos\MiDocumento.doc")</code><br />
Espero que os sea útil ;)ElPaterhttp://www.blogger.com/profile/16022680446046453947noreply@blogger.com12tag:blogger.com,1999:blog-162184249783410837.post-19072379039550375352011-04-05T12:51:00.004+02:002011-08-03T16:42:10.673+02:00Optimizar Query SQL con UNION ALL<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiRxoeeycTTjZEPVCeDLSC7Gq82LU-653K8VUKMG2oXmyfRuh5oIuMCtfjxpeHdB0Bb79QKU_e493LZB_yhfaRYyfXvsdApMb6vordeK1U5peOxddkRRhy-uE5kB8r08bNi7D0h2qsB4F0/s1600/website-optimization_64.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiRxoeeycTTjZEPVCeDLSC7Gq82LU-653K8VUKMG2oXmyfRuh5oIuMCtfjxpeHdB0Bb79QKU_e493LZB_yhfaRYyfXvsdApMb6vordeK1U5peOxddkRRhy-uE5kB8r08bNi7D0h2qsB4F0/s1600/website-optimization_64.png" /></a></div>Pongamos un ejemplo de una query de SQL en que queremos traer las filas de <i>TABLA1</i> y <i>TABLA2</i>. Usamos <i><b>UNION</b></i> para unir las dos consultas. Hasta aquí todo correcto. Pero se puede optimizar si sabemos lo que vamos a obtener.<br />
<br />
<br />
Ejemplo:<br />
<code><span class="Apple-style-span" style="color: #38761d;">SELECT</span> NOMBRE, APELL <span class="Apple-style-span" style="color: #38761d;">FROM</span> TABLA_EMPLEADOS<br />
<span class="Apple-style-span" style="color: blue;"> UNION</span><br />
<span class="Apple-style-span" style="color: #38761d;"> SELECT</span> NOM, APE <span class="Apple-style-span" style="color: #38761d;">FROM</span> TABLA_CLIENTES</code><br />
Cuando se manda la sentencia al motor de la Base de Datos, realmente lo que hace es consultar <i>TABLA1</i> con sus registros, consulta <i>TABLA2</i> con sus registros, los junta, los ordena y les aplica un <i>DISTINCT</i>. Estas dos últimas tareas son las que ejercen más trabajo sobre el motor de Base de Datos, y las que más recursos gastan.<br />
<br />
Si sabemos que los registros que vamos a obtener de <i>TABLA1</i> y <i>TABLA2</i> son completamente distintos, podemos usar <b><i><span class="Apple-style-span" style="color: #38761d;">UNION ALL</span></i></b>, lo que evita que haga la ordenación y el <i>DISTINCT</i> en el motor de Base de Datos. <br />
<br />
<code><span class="Apple-style-span" style="color: #38761d;">SELECT</span> NOMBRE, APELL <span class="Apple-style-span" style="color: #38761d;">FROM</span> TABLA_EMPLEADOS<br />
<b><span class="Apple-style-span" style="color: blue;">UNION ALL</span></b><br />
<span class="Apple-style-span" style="color: #38761d;"> SELECT</span> NOM, APE <span class="Apple-style-span" style="color: #38761d;">FROM</span> TABLA_CLIENTES</code><br />
Al hacer el <b><i><span class="Apple-style-span" style="color: #38761d;">UNION ALL</span></i></b>, realmente lo que se pide es "traeme todos los registros, aunque esten duplicados". Pero si sabemos que no van a devolverse registros duplicados, usando esta variante obtendremos el mismo resultado, pero de forma más eficiente.ElPaterhttp://www.blogger.com/profile/16022680446046453947noreply@blogger.com0tag:blogger.com,1999:blog-162184249783410837.post-59505626336686578662011-04-04T16:29:00.007+02:002011-08-03T16:44:24.473+02:00Bienvenidos<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjQVuCTPwkQP_KyG-ebwpOrhsR7Xcr8JzOi3Mj2NakXKTjMy6-p5H-B6f9oVa-wa3d6-2pJfvxx58Jt13iLtwE6QYJwZUWC7PdLMBu0Qhf7mlKHVlGWBz-0Ks2tm5mlKCFCtd-af8WKuRw/s1600/puzzle.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjQVuCTPwkQP_KyG-ebwpOrhsR7Xcr8JzOi3Mj2NakXKTjMy6-p5H-B6f9oVa-wa3d6-2pJfvxx58Jt13iLtwE6QYJwZUWC7PdLMBu0Qhf7mlKHVlGWBz-0Ks2tm5mlKCFCtd-af8WKuRw/s1600/puzzle.png" /></a></div>Soy tan sólo un programador más, que trabaja con lenguaje PowerBuilder desde hace 12 años. Y cómo uno más, tengo que buscar por internet a ver si a alguien se le ha ocurrido cómo hacer X pirueta para desarrollar alguna utilidad complicada, o cómo sacar el máximo jugo a este lenguaje.<br />
<br />
Es por esto que me he animado a abrir este blog. Tanto para ayuda de los lectores como para la mía propia y que me sirva de repositorio de código útil. Habrá entradas sobre "how-to" de <b>PowerBuilder</b> y <b>SQL</b>, trucos para realizar esas cosas que creías imposible con este lenguaje. También sobre <b>retoque de imágenes</b> y <b>usabilidad</b>, y cómo he conseguido hacer aplicaciones realmente cómodas, rápidas y útiles.<br />
<br />
Espero que todo este código le resulte útil a algún otro <i>Pica-teclas</i> como yo, que nos dedicamos a machacar teclados y neuronas para desarrollar aplicaciones que solventen los problemas de nuestros usuarios.<br />
<br />
Bienvenidos al blog. Empezamos a <a href="http://picarpiezas.blogspot.com/">Picar Piezas</a> en 3...2...1... COMMIT;ElPaterhttp://www.blogger.com/profile/16022680446046453947noreply@blogger.com0