Archivos de Categoría: Base de datos - Paginas 8

Error al guardar los cambios de una tabla

Usando el cliente de SQL server en sus últimas versiones, al intentar modificar el diseño de una tabla nos puede salir un error del tipo «No se pueden guardar los cambio, toca borrar la tabla y volver a crearla». Esto si estamos en un entorno de producción es lo ideal para que no pasen «accidentes» pero para un entorno de desarrollo da bastante la lata.
Las alternativas que tenemos es hacerlo a manita con la sentencia ALTER TABLE, que si es muy guay pero bastante coñazo. Lo mejor es activar la opción de guardar los cambios (copiado de http://support.microsoft.com/kb/956176):

  1. Abra SQL Server Management Studio (SSMS).
  2. En el menú Herramientas, haga clic en Opciones.
  3. En el panel de navegación de la ventana de Opciones, haga clic en diseñadores.
  4. Active o desactive la casilla de verificación impedir guardar cambios que requieran volver a crear la tablas y, a continuación, haga clic en Aceptar

Procedimiento para importar CSV directamente

Hoy me he marcado un procedimiento para importar directamente un CSV.

create procedure [dbo].[actualizaStock]
@fichero varchar(500),
@resultado int out
as

BEGIN TRANSACTION;
BEGIN TRY

-- si existe la tabla donde haremos la importación, la vaciamos, sino la creamos
if exists (select * from sys.tables where name = 'tmp_stock') 
	truncate table tmp_stock
else
	CREATE TABLE dbo.tmp_stock(
		codproducto varchar(15) NULL,
		talla varchar(5) NULL,
		unidades int NULL
	) ON [PRIMARY]

-- Importamos el fichero, especificando los delimitadores de columna y de fila. 
-- Se hace con un execute porque al hacer una importación con BULK no podemos poner la ruta como una variable  
execute ('BULK INSERT dbo.tmp_stock FROM ''' + @fichero + ''' WITH (FIRSTROW = 2, FIELDTERMINATOR = '';'', ROWTERMINATOR = ''\n'')');


-- en este caso actualizamos un stock con los datos que acabamos de importar 
update s
set s.stock = s.stock + t.unidades
from stock s inner join tmp_stock t on t.codproducto = s.CodProducto and s.Talla = t.talla

-- vamos a guardar los datos importados en una nueva tabla con la fecha del día
declare @tabla varchar(50)
set @tabla = 'tmp_stock_' + convert(varchar(8), getdate(), 112);

-- si ya existe una tabla con el nuevo nombre, la eliminamos
if exists (select * from sys.tables where name = @tabla) 
	execute ('drop table ' + @tabla);

-- renombramos la tabla que hemos usado para la importación con el nuevo nombre 
exec sp_RENAME 'tmp_stock' , @tabla	


END TRY
BEGIN CATCH    
    IF @@TRANCOUNT > 0
		begin
			set @resultado = -1
			ROLLBACK TRANSACTION;
			return -1
		end
END CATCH;

IF @@TRANCOUNT > 0
	begin
		set @resultado = 0
		COMMIT TRANSACTION;
		return 0
	end

Geolocalización en SQL Server

Para cierto proyecto con el que estoy, toca localizar la tienda más cercana a tu posición actual por lo que me he puesto a investigar el tipo de datos de geolocalización que tiene SQL server y que todavía no lo había usado. En vez de guardar latitud y longitud, guarda los datos en su propio formato por lo que toca transformar los valores antes de insertarlos en base de datos:


DECLARE @origen GEOGRAPHY
SET @origen = GEOGRAPHY::STPointFromText('POINT(40.455013 -3.597037)',4326)

Y para calcular la distancia entre dos puntos, solo hay que hacerlo de la siguiente forma:


DECLARE @origen GEOGRAPHY
SET @origen = GEOGRAPHY::STPointFromText('POINT(40.455013 -3.597037)',4326)
DECLARE @destino GEOGRAPHY
SET @destino = GEOGRAPHY::STPointFromText('POINT(42.341155 -7.527836)',4326)

SELECT @origen.STDistance(@destino)/1000 'Km';

Y ya como ejemplo práctico, si tenemos una tabla con los posicionamientos y queremos conocer el punto más cercano a otro dado:

-- tabla donde tenemos las distintas posiciones
DECLARE @ciudades table (
 ciudad varchar(50),
 posicion GEOGRAPHY
)

insert into @ciudades (ciudad, posicion) values 
 ('Madrid', GEOGRAPHY::STPointFromText('POINT(40.455013 -3.597037)',4326))
,('Pradomao', GEOGRAPHY::STPointFromText('POINT(42.341155 -7.527836)',4326))
,('Guadalajara', GEOGRAPHY::STPointFromText('POINT(40.655639 -3.175507)',4326))
,('Andorra', GEOGRAPHY::STPointFromText('POINT(42.585444 1.600341)',4326))

-- el origen desde donde haremos el calculo
declare @origen GEOGRAPHY
set @origen = GEOGRAPHY::STPointFromText('POINT(40.420357 -3.531518)',4326)

select ciudad, posicion.STDistance(@origen)/1000 as 'Km'
from @ciudades order by posicion.STDistance(@origen) 

O en mi caso que no tenemos el campo de geolocalización sino la longitud y la latitud:

-- tabla donde tenemos las distintas posiciones
DECLARE @ciudades table (
 ciudad varchar(50),
 longitud varchar(50),
 latitud varchar(50)
)

insert into @ciudades (ciudad, longitud, latitud) values 
 ('Madrid', '40.455013', '-3.597037')
,('Pradomao', '42.341155', '-7.527836')
,('Guadalajara', '40.655639', '-3.175507')
,('Andorra',  '42.585444', '1.600341')

-- el origen desde donde haremos el calculo
declare @origen GEOGRAPHY
set @origen = GEOGRAPHY::STPointFromText('POINT(40.420357 -3.531518)',4326)


select ciudad, geography::STPointFromText('POINT(' + CAST(longitud AS VARCHAR(20)) + ' ' + CAST(latitud AS VARCHAR(20)) + ')', 4326).STDistance(@origen)/1000 as 'Km'
from @ciudades 
order by geography::STPointFromText('POINT(' + CAST(longitud AS VARCHAR(20)) + ' ' + CAST(latitud AS VARCHAR(20)) + ')', 4326).STDistance(@origen) 

Más sencillo imposible