Mostrando entradas con la etiqueta tips. Mostrar todas las entradas
Mostrando entradas con la etiqueta tips. Mostrar todas las entradas

jueves, 4 de noviembre de 2010

Enviar ficheros por FTP usando Powershell

Con este script de Powershell podemos automatizar el envío por FTP de todos los ficheros que existan en un directorio y cumplan con un determinado filtro (por ejemplo, todos los .txt).

Me he basado en este post para hacerlo: How to upload a file, the PowerShell way

Actualización 17/11/2010: añado $R.proxy = $null para que no de problemas con proxys HTTP :)


#Directorio del que obtenemos los ficheros
$source_folder="c:\temp\"

#Filtro que deben cumplir los ficheros que se envían
$files_filter=$source_folder + "*.txt"

#Archivo donde se guardará el log del proceso
$log_file="c:\FTP.log"

#URL de nuestro servidor FTP
$FTP_url="ftp://myserver.ftp/destino/"
$FTP_user="usuario"
$FTP_password="password"

# Obtenemos todos los ficheros del directorio que cumplan con el filtro
foreach ($filename in Get-Childitem $files_filter)
{
#Path completo del fichero de origen que se enviará por FTP
$Source=$source_folder + $filename.name

#Guardamos una línea en el archivo de logs
$DATE=Get-Date
Add-Content $log_file "[INFO]: $DATE - $Source"

#Path completo del fichero de destino
$Destination=$FTP_url + $filename.name

#Aquí comienza el control de errores, se verá más abajo con el "trap"
&{
#Usamos el metodo estático Create para crear una petición web.
#Pasamos el destino como argumento y lo convertimos
#en un objeto FtpWebRequest.
$R=[System.Net.FtpWebRequest][System.Net.WebRequest]::Create($Destination)

#Especificamos a la petición cómo nos loguearemos
#(usando un objeto NetworkCredential)
$R.Credentials = New-Object System.Net.NetworkCredential($FTP_user, $FTP_password)
$R.proxy = $null
#... y qué tipo de método se usará. Un file upload.
$R.Method = "STOR"

#Aquí se usa el método más simple para obtener los bytes
#de un fichero a un array de bytes.
$FileContens = [System.IO.File]::ReadAllBytes($Source)

#Finalmente, se ponen el array de bytes en la petición web.
$S = $R.GetRequestStream()
$S.Write($FileContens, 0, $FileContens.Length)

#Se cierran el stream y se libera el objeto
$S.Close()
$S.Dispose()
}
trap [SystemException]
{
#Si se produce un error, lo guardamos en el fichero de logs
#y continuamos con el siguiente fichero
$ERROR=$_
$DATE=Get-date
Add-Content $log_file "[ERROR]: $DATE - $ERROR"
continue
}
}



Ahora lo suyo es guardarnos el script en un fichero .ps1 y crear una tarea programada que lo ejecute, por ejemplo, que cada media hora compruebe el directorio y envíe los ficheros.

Y ya que estamos, podemos crear la tarea programada con Powershell:

SCHTASKS /Create /RL HIGHEST /SC MINUTE /MO 30 /TN "Enviar ficheros por FTP" /NP /TR 'powershell.exe –Command "c:\enviar_ficheros_FTP.ps1"'

Bola extra
Tal vez os interese también obtener los ficheros de un FTP: How to download a file, the PowerShell way

Happy codding ;)

martes, 2 de marzo de 2010

T-SQL: SET ROWCOUNT vs. SELECT TOP

“Si hay 2 formas de hacer lo mismo, una seguro que da problemas.”
No sé si esta es una de las Leyes de Murphy, pero debería serlo :P

SET ROWCOUNT es uno de esos settings “con trampa” que tiene T-SQL y que si no sabes usarlos adecuadamente te puede provocar muchos quebraderos de cabeza.

Según MSDN: Hace que SQL Server detenga el procesamiento de la consulta una vez que se han devuelto las filas especificadas.

Ah, qué bonito, si sólo quiero que una consulta me devuelva 20 registros pues pongo “SET ROWCOUNT 20;” antes de ejecutarla y listo.
Vamos, lo mismo que si hago un SELECT TOP 20 …

Bueno, sabiendo esto se puede elegir entre utilizar uno u otro método para limitar los resultados. Pero SET ROWCOUNT, al tratarse de un setting afecta a TODAS las consultas que se ejecutan dentro del mismo contexto y aquí es donde viene el problema.

Por ejemplo, supongamos que tenemos 2 tablas relacionadas y una función llamada MiFuncion() que hace lo siguiente:


SELECT idPadre FROM TablaPadre WHERE campoPadre = @parametro


Ahora bien, si hacemos esto:


SET ROWCOUNT 10;
SELECT * FROM TablaHija WHERE idPadre in (select idPadre from MiFuncion(@parametro))


La llamada a MiFuncion (@parametro) devolverá únicamente 10 registros, y a su vez la SELECT inicial también devolverá 10 registros, lo que puede provocar que no obtengamos el resultado deseado :(

¿Y cómo se puede solucionar esto? Pues es relativamente sencillo, cambiamos la llamada a la función por una subconsulta:


SET ROWCOUNT 10;
SELECT * FROM TablaHija WHERE idPadre in (SELECT idPadre FROM TablaPadre WHERE campoPadre = @parametro)


Y así, la subconsulta devuelve los registros que tenga que devolver, sin tener en cuenta el número de SET ROWCOUNT y la SELECT sí que devuelve 10 registros O_o El que lo entienda que me lo explique.

Así que ya sabéis, si vais a usar SET ROWCOUNT, hacerlo con cuidado y aseguraros de desactivarla usando SET ROWCOUNT 0. En cualquier caso, es menos peligroso usar SELECT TOP, ya que solo afecta a la consulta en la que se especifica.

Tampoco es muy aconsejable mezclar SELECT TOP … con SET ROWCOUNT … porque SET ROWCOUNT anula la palabra clave TOP de la instrucción SELECT si el número de filas es inferior.

Happy codding ;)

BOLA EXTRA: a partir de la próxima versión de SQL Server (la que siga a la 2008) la utilización de SET ROWCOUNT no afectará a las instrucciones DELETE, INSERT ni UPDATE.
No me quiero ni imaginar los problemas que puede provocar SET ROWCOUNT con un UPDATE O_o

viernes, 26 de febrero de 2010

Otro test de agudeza visual: ¿qué código es peor?

Opción 1:


Dim intVariable as Integer

Try
intVariable = LlamadaaMetodoQuePuedeDevolverNothing()
Catch ex as Exception
intVariable = 0
End Try



Opción 2:


Dim intVariable as Integer = 0

Try
intVariable = LlamadaaMetodoQuePuedeDevolverNothing()
Catch ex as Exception

End Try





Respuesta: la opción 2, que provoca un precioso "agujero negro de excepciones".

PD: los 2 ejemplos están basados en hechos reales, que no tengo tanta imaginación :P

jueves, 14 de enero de 2010

Search in text fields with T-SQL: beyond LIKE

Versión en castellano

One of the first things you learn when begin with T-SQL is to use the LIKE clause to make comparisons in text fields (char, nchar, varchar, nvarchar or text). But this clause is quite limited and don’t have good performance. So, what can be used instead of LIKE?

A good option is to use CONTAINS(that to be honest, I discovered by accident: P), which allows us to refine more comparisons and offers much better performance.


To give an example of using CONTAINS I will use the PUBS database (Microsoft offers as an example with SQL Server and can be downloaded from here) and we will find records in the "jobs" table filtering by "job_desc" field.

To give you an idea (or if you don't want download example databases) the "jobs" table has the following records:


job_id job_desc min_lvl max_lvl
-------- -------------------------------------------------- ------- -------
1 New Hire - Job not specified 10 10
2 Chief Executive Officer 200 250
3 Business Operations Manager 175 225
4 Chief Financial Officier 175 250
5 Publisher 150 250
6 Managing Editor 140 225
7 Marketing Manager 120 200
8 Public Relations Manager 100 175
9 Acquisitions Manager 75 175
10 Productions Manager 75 165
11 Operations Manager 75 150
12 Editor 25 100
13 Sales Representative 25 100
14 Designer 25 100


Before using CONTAINS we must create a FULLTEXT index in the table, some like this:


CREATE FULLTEXT CATALOG ft AS DEFAULT;
CREATE FULLTEXT INDEX ON Jobs(job_desc)
KEY INDEX PK__jobs__173876EA
GO


PK__jobs__173876EA is the table’s PRIMARY KEY.


Once this is done we can start with examples:

Search exact word

Obtains all records containing the word 'manager' in "job_desc" field.
Note: is not case sensitive.


select * from jobs where CONTAINS( job_desc, 'manager' )

job_id job_desc min_lvl max_lvl
-------- -------------------------------------------------- ------- -------
3 Business Operations Manager 175 225
7 Marketing Manager 120 200
8 Public Relations Manager 100 175
9 Acquisitions Manager 75 175
10 Productions Manager 75 165
11 Operations Manager 75 150


Also you can search for phrases (more than 1 word) and separators are ignored.


select * from jobs where CONTAINS (job_desc, '"hire job"');

job_id job_desc min_lvl max_lvl
-------- -------------------------------------------------- ------- -------
1 New Hire - Job not specified 10 10


Search by approximation with several options

Finds the records containing any word beginning with 'pub' or 'edit'.


select * from jobs where CONTAINS(job_desc, '"pub*" OR "edit*"');

job_id job_desc min_lvl max_lvl
-------- -------------------------------------------------- ------- -------
5 Publisher 150 250
6 Managing Editor 140 225
8 Public Relations Manager 100 175
12 Editor 25 100


CONTAINS supports AND, AND NOT, OR.

Search a word near another one

Finds records that contain a word beginning with 'pub' and that word is near 'manager'.


select * from jobs where CONTAINS (job_desc, '"pub*" NEAR manager')

job_id job_desc min_lvl max_lvl
-------- -------------------------------------------------- ------- -------
8 Public Relations Manager 100 175


CONTAINS can also search for synonyms and derived words, but this requires using a thesaurus.

Happy codding ;)

Extra ball
If you find CONTAINS interested, see also FREETEXT and CONTAINSTABLE.

miércoles, 13 de enero de 2010

3 ways to find SQL Server version with T-SQL

Versión en castellano

In a perfect world all our facilities have the same versions of the applications, database engines and auxiliary libraries, but this world is a utopia, and we sometimes have to find out what version of the database engine is working or what dll version is installed.

In the case of SQL Server I have discovered 3 ways to find the version from T-SQL, with any of the 3 options will get the full version, but in most cases only need the "major release". For example if you want to execute features of the new versions, or ones that are obsolete in the latest version but still work in older versions.

Let’s go:

@@VERSION

Is a system variable, so is useful for any version of SQL Server, but from the 2005 version returns a nvarchar instead of varchar from previous versions.


declare @version char(2)

select @version = substring(@@version, charindex('- ', @@version) + 2, charindex('.',@@version) - charindex('- ', @@version)-2)


SERVERPROPERTY(‘ProductVersion’)

This option is only available after SQL Server 2000 and returns a value of type sqlvariant, so you have to convert it to varchar or nvarchar to get the main version.


declare @productversion varchar(50)

declare @version char(2)

select @productversion = CAST(SERVERPROPERTY('productversion') AS VARCHAR(50))

set @version = SUBSTRING(@productversion,1,CHARINDEX('.',@productversion)-1)



xp_msver ‘ProductVersion’

Perhaps the most complicated, because it returns a result set like this:


Index Name Internal_Value Character_Value
------ -------------------------------- -------------- ----------------------
2 ProductVersion 589824 9.00.1406.00

(1 row(s) affected)


But you can also get the version with something like this:

CREATE TABLE #VersionTable(
[Index] int,
Name varchar(30),
Internal_Value int,
Character_Value varchar(50))
GO

INSERT INTO #VersionTable
EXEC master..xp_msver 'ProductVersion'

DECLARE @Version char(2)

SELECT @Version = (
SELECT SUBSTRING(Character_Value,1,CHARINDEX('.', Character_Value)-1)
FROM #VersionTable)

drop table #VersionTable


With any of the 3 options we get the following in @Version:

‘7’ -> SQL Server 7
‘8’ -> SQL Server 2000 y MSDE
‘9’ -> SQL Server 2005
‘10’ -> SQL Server 2008

Happy coding ;)