¿Hay alguna forma de hacer que una variable TSQL sea constante?
¿Hay alguna forma de hacer que una variable TSQL sea constante?
Respuestas:
No, pero puede crear una función y codificarla allí y usarla.
Aquí hay un ejemplo:
CREATE FUNCTION fnConstant()
RETURNS INT
AS
BEGIN
RETURN 2
END
GO
SELECT dbo.fnConstant()
WITH SCHEMABINDING
en la CREATE FUNCTION
declaración (en lugar de en un procedimiento almacenado que podría estar llamando a la función), ¿es así?
Una solución que ofrece Jared Ko es utilizar pseudoconstantes .
Como se explica en SQL Server: ¿variables, parámetros o literales? ¿O… constantes? :
Las pseudoconstantes no son variables ni parámetros. En cambio, son simplemente vistas con una fila y suficientes columnas para admitir sus constantes. Con estas reglas simples, el motor SQL ignora por completo el valor de la vista, pero aún crea un plan de ejecución basado en su valor. ¡El plan de ejecución ni siquiera muestra una unión a la vista!
Crea así:
CREATE SCHEMA ShipMethod GO -- Each view can only have one row. -- Create one column for each desired constant. -- Each column is restricted to a single value. CREATE VIEW ShipMethod.ShipMethodID AS SELECT CAST(1 AS INT) AS [XRQ - TRUCK GROUND] ,CAST(2 AS INT) AS [ZY - EXPRESS] ,CAST(3 AS INT) AS [OVERSEAS - DELUXE] ,CAST(4 AS INT) AS [OVERNIGHT J-FAST] ,CAST(5 AS INT) AS [CARGO TRANSPORT 5]
Entonces usa así:
SELECT h.* FROM Sales.SalesOrderHeader h JOIN ShipMethod.ShipMethodID const ON h.ShipMethodID = const.[OVERNIGHT J-FAST]
O así:
SELECT h.* FROM Sales.SalesOrderHeader h WHERE h.ShipMethodID = (SELECT TOP 1 [OVERNIGHT J-FAST] FROM ShipMethod.ShipMethodID)
Mi solución a las constantes faltantes es dar pistas sobre el valor del optimizador.
DECLARE @Constant INT = 123;
SELECT *
FROM [some_relation]
WHERE [some_attribute] = @Constant
OPTION( OPTIMIZE FOR (@Constant = 123))
Esto le dice al compilador de consultas que trate la variable como si fuera una constante al crear el plan de ejecución. La desventaja es que tienes que definir el valor dos veces.
No, pero se deben usar las buenas convenciones de nomenclatura.
declare @MY_VALUE as int
FN_CONSTANT()
. De esa forma queda claro lo que está haciendo.
No hay soporte integrado para constantes en T-SQL. Puede usar el enfoque de SQLMenace para simularlo (aunque nunca puede estar seguro de si alguien más ha sobrescrito la función para devolver algo más ...), o posiblemente escribir una tabla que contenga constantes, como se sugiere aquí . ¿Quizás escribir un disparador que revierte cualquier cambio en la ConstantValue
columna?
Antes de utilizar una función de SQL, ejecute el siguiente script para ver las diferencias en el rendimiento:
IF OBJECT_ID('fnFalse') IS NOT NULL
DROP FUNCTION fnFalse
GO
IF OBJECT_ID('fnTrue') IS NOT NULL
DROP FUNCTION fnTrue
GO
CREATE FUNCTION fnTrue() RETURNS INT WITH SCHEMABINDING
AS
BEGIN
RETURN 1
END
GO
CREATE FUNCTION fnFalse() RETURNS INT WITH SCHEMABINDING
AS
BEGIN
RETURN ~ dbo.fnTrue()
END
GO
DECLARE @TimeStart DATETIME = GETDATE()
DECLARE @Count INT = 100000
WHILE @Count > 0 BEGIN
SET @Count -= 1
DECLARE @Value BIT
SELECT @Value = dbo.fnTrue()
IF @Value = 1
SELECT @Value = dbo.fnFalse()
END
DECLARE @TimeEnd DATETIME = GETDATE()
PRINT CAST(DATEDIFF(ms, @TimeStart, @TimeEnd) AS VARCHAR) + ' elapsed, using function'
GO
DECLARE @TimeStart DATETIME = GETDATE()
DECLARE @Count INT = 100000
DECLARE @FALSE AS BIT = 0
DECLARE @TRUE AS BIT = ~ @FALSE
WHILE @Count > 0 BEGIN
SET @Count -= 1
DECLARE @Value BIT
SELECT @Value = @TRUE
IF @Value = 1
SELECT @Value = @FALSE
END
DECLARE @TimeEnd DATETIME = GETDATE()
PRINT CAST(DATEDIFF(ms, @TimeStart, @TimeEnd) AS VARCHAR) + ' elapsed, using local variable'
GO
DECLARE @TimeStart DATETIME = GETDATE()
DECLARE @Count INT = 100000
WHILE @Count > 0 BEGIN
SET @Count -= 1
DECLARE @Value BIT
SELECT @Value = 1
IF @Value = 1
SELECT @Value = 0
END
DECLARE @TimeEnd DATETIME = GETDATE()
PRINT CAST(DATEDIFF(ms, @TimeStart, @TimeEnd) AS VARCHAR) + ' elapsed, using hard coded values'
GO
2760ms elapsed, using function
| 2300ms elapsed, using local variable
| 2286ms elapsed, using hard coded values
|
5570 elapsed, using function
| 406 elapsed, using local variable
| 383 elapsed, using hard coded values
| 3893 elapsed, using function without schemabinding
select top 1 @m = cv_val from code_values where cv_id = 'C101'
y lo mismo ... 'C201'
donde code_values es una tabla de diccionario con 250 vars, todos estaban en SQL-Server 2016
Si está interesado en obtener un plan de ejecución óptimo para un valor en la variable, puede usar un código SQL dinámico. Hace que la variable sea constante.
DECLARE @var varchar(100) = 'some text'
DECLARE @sql varchar(MAX)
SET @sql = 'SELECT * FROM table WHERE col = '''+@var+''''
EXEC (@sql)
Para enumeraciones o constantes simples, una vista con una sola fila tiene un gran rendimiento y control de tiempo de compilación / seguimiento de dependencia (porque es un nombre de columna)
Consulte la publicación del blog de Jared Ko https://blogs.msdn.microsoft.com/sql_server_appendix_z/2013/09/16/sql-server-variables-parameters-or-literals-or-constants/
crear la vista
CREATE VIEW ShipMethods AS
SELECT CAST(1 AS INT) AS [XRQ - TRUCK GROUND]
,CAST(2 AS INT) AS [ZY - EXPRESS]
,CAST(3 AS INT) AS [OVERSEAS - DELUXE]
, CAST(4 AS INT) AS [OVERNIGHT J-FAST]
,CAST(5 AS INT) AS [CARGO TRANSPORT 5]
usa la vista
SELECT h.*
FROM Sales.SalesOrderHeader
WHERE ShipMethodID = ( select [OVERNIGHT J-FAST] from ShipMethods )
Esta bien, veamos
Las constantes son valores inmutables que se conocen en el momento de la compilación y no cambian durante la vida del programa.
eso significa que nunca puede tener una constante en SQL Server
declare @myvalue as int
set @myvalue = 5
set @myvalue = 10--oops we just changed it
el valor acaba de cambiar
Dado que no hay soporte integrado para constantes, mi solución es muy simple.
Dado que esto no es compatible:
Declare Constant @supplement int = 240
SELECT price + @supplement
FROM what_does_it_cost
Simplemente lo convertiría a
SELECT price + 240/*CONSTANT:supplement*/
FROM what_does_it_cost
Obviamente, esto se basa en que todo (el valor sin espacio final y el comentario) sea único. Cambiarlo es posible con una búsqueda y reemplazo global.
No existe tal cosa como "crear una constante" en la literatura de bases de datos. Las constantes existen tal como son y, a menudo, se las llama valores. Se puede declarar una variable y asignarle un valor (constante). Desde un punto de vista escolástico:
DECLARE @two INT
SET @two = 2
Aquí @two es una variable y 2 es un valor / constante.
2
se traduce a un valor binario cuando se asigna en el "tiempo de compilación". El valor real codificado depende del tipo de datos al que se está asignando (int, char, ...).
La mejor respuesta es de SQLMenace de acuerdo con el requisito si se trata de crear una constante temporal para su uso dentro de los scripts, es decir, en múltiples sentencias / lotes de GO.
Simplemente cree el procedimiento en tempdb, entonces no tendrá ningún impacto en la base de datos de destino.
Un ejemplo práctico de esto es un script de creación de base de datos que escribe un valor de control al final del script que contiene la versión del esquema lógico. En la parte superior del archivo hay algunos comentarios con el historial de cambios, etc. Pero en la práctica, la mayoría de los desarrolladores se olvidarán de desplazarse hacia abajo y actualizar la versión del esquema en la parte inferior del archivo.
El uso del código anterior permite definir una constante de versión de esquema visible en la parte superior antes de que el script de la base de datos (copiado de la función de generar scripts de SSMS) cree la base de datos pero se use al final. Esto está justo en la cara del desarrollador junto al historial de cambios y otros comentarios, por lo que es muy probable que lo actualicen.
Por ejemplo:
use tempdb
go
create function dbo.MySchemaVersion()
returns int
as
begin
return 123
end
go
use master
go
-- Big long database create script with multiple batches...
print 'Creating database schema version ' + CAST(tempdb.dbo.MySchemaVersion() as NVARCHAR) + '...'
go
-- ...
go
-- ...
go
use MyDatabase
go
-- Update schema version with constant at end (not normally possible as GO puts
-- local @variables out of scope)
insert MyConfigTable values ('SchemaVersion', tempdb.dbo.MySchemaVersion())
go
-- Clean-up
use tempdb
drop function MySchemaVersion
go
WITH SCHEMABINDING
debería convertir esto en una constante 'real' (un requisito para que una UDF se considere determinista en SQL). Es decir, debería aterrizar en caché. Aún así, +1.