Archivos de Categoría: SQL Server - Paginas 4

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

Resultado de una consulta en formato JSON

Navegando por distintos foros me he encontrado esta maravilla, un script de SQL server que a partir de una consulta nos devuelve el resultado en formato JSON.

-- la consulta 
select *
into #json_base
from tabla;

-- a partir de aqui no tocar
declare @columns table (
    id int identity primary key,
    name sysname,
    datatype sysname,
    is_number bit,
    is_date bit);

insert into @columns(name, datatype, is_number, is_date)
select columns.name, types.name,
       case when number_types.name is not NULL then 1 else 0
       end as is_number,
       case when date_types.name is not NULL then 1 else 0
       end as is_date
from tempdb.sys.columns
join tempdb.sys.types
    on (columns.system_type_id = types.system_type_id)
left join (values ('int'), ('real'), ('numeric'), 
                  ('decimal'), ('bigint'), ('tinyint')) as number_types(name)
    on (types.name = number_types.name)
left join (values ('date'), ('datetime'), ('datetime2'),
                  ('smalldatetime'), ('time'), ('datetimeoffset')) as date_types(name)
    on (types.name = date_types.name)
where object_id = OBJECT_ID('tempdb..#json_base');

declare @field_list varchar(max) = STUFF((
    select '+'',''+' + QUOTENAME(QUOTENAME(name, '"') + ':', '''')
           + '+' + case when is_number = 1
                        then 'COALESCE(LTRIM('
                                + QUOTENAME(name) + '),''null'')'
                        when is_date = 1
                        then 'COALESCE(QUOTENAME(LTRIM(convert(varchar(max), '
                                + QUOTENAME(name) + ', 126)),''"''),''null'')'
                        else 'COALESCE(QUOTENAME('
                                + QUOTENAME(name) + ',''"''),''null'')'
                   end
    from @columns
    for xml path('')),
    1, 5, '');

create table #json_result (
    id int identity primary key,
    line varchar(max));

declare @sql varchar(max) = REPLACE(
    'insert into #json_result '
  + 'select '',{''+{f}+''}'' '
  + 'from #json_base', '{f}', @field_list);

exec(@sql);

update #json_result
set line = STUFF(line, 1, 1, '')
where id = 1;

select '['
UNION ALL
select line
from #json_result
UNION ALL
select ']';

drop table #json_base;
drop table #json_result;

Por cierto, lo he encontrado aquí