Dha*_*val 1 sql-server-2008 sql-server t-sql
我给出了两个表格的简化版本,如下所示。
表记录
ID unit val
--- --- ---
1 KV 3
1 BAR 4
2 KV 7
2 KG 5
3 C 37
Run Code Online (Sandbox Code Playgroud)
表单位
unit convUnit formula
--- --- ---
KV CV @val * .865
KG lbs @val / 2.205
BAR PSI @val / 14.504
C F @val * 9/5 + 32
Run Code Online (Sandbox Code Playgroud)
我将无法更改表结构。
我知道哪个单位将转换为哪个单位(即我知道 KV 将转换为 CV,KG 将转换为磅等..)。
请注意,公式是一varchar
列,我从表格tblRecords.unit
中引用了tblUnit.unit
但我的问题是如何在单个查询中动态计算公式。即我想要以下输出。
ID unit val convVal(column will be counted on the fly using formula stored in tblUnit.formula field)
--- --- --- ---
1 KV 3 2.595
1 BAR 4 0.27578599
2 KV 7 6.055
2 KG 5 2.267573696
3 C 37 98.6
Run Code Online (Sandbox Code Playgroud)
在 SQL Server 中没有直接的方法来实现这一点,因为函数中不允许使用动态 SQL。然而,由于你的 tblUnit 很可能很少改变,你可以采取间接的方式:
我们需要的是一个能够根据表中的公式进行转换的函数。如果我们能够在函数中对这些公式进行硬编码,并在每次表更改时以某种方式更新函数,那么问题就可以解决。那么,我们可以吗?
是的,我们可以:SQL Fiddle
MS SQL Server 2008 架构设置:
CREATE TABLE dbo.tblRecords
([ID] int, [unit] varchar(3), [val] DECIMAL(10,4))
;
INSERT INTO dbo.tblRecords
([ID], [unit], [val])
VALUES
(1, 'KV', 3),
(1, 'BAR', 4),
(2, 'KV', 7),
(2, 'KG', 5),
(3, 'C', 37)
;
CREATE TABLE dbo.tblUnit
([unit] varchar(3), [convUnit] varchar(3), [formula] varchar(15))
;
GO
CREATE FUNCTION dbo.fnConvert(@unit VARCHAR(3),@val DECIMAL(10,4))
RETURNS TABLE
AS
RETURN
SELECT @val convVal;
GO
CREATE TRIGGER updateFnConvert ON dbo.tblUnit AFTER INSERT,UPDATE,DELETE
AS
BEGIN
DECLARE @cmd NVARCHAR(MAX);
SELECT @cmd = 'ALTER FUNCTION dbo.fnConvert(@unit VARCHAR(3),@val DECIMAL(10,4))'+
' RETURNS TABLE AS'+
' RETURN SELECT CASE @unit'+
(SELECT ' WHEN '''+unit+''' THEN '+formula
FROM dbo.tblUnit
FOR XML PATH(''),TYPE).value('.','NVARCHAR(MAX)') +
' END convVal;'
EXEC(@cmd);
END
GO
Run Code Online (Sandbox Code Playgroud)
这将在 tblUnit 表上创建两个表:一个 fnConvert 函数存根和一个触发器。
当我们现在使用 INSERT、UPDATE 或 DELETE 更改表的内容时,如下所示:
查询1:
INSERT INTO dbo.tblUnit
([unit], [convUnit], [formula])
VALUES
('KV', 'CV', '@val * .865'),
('KG', 'lbs', '@val / 2.205'),
('BAR', 'PSI', '@val / 14.504'),
('C', 'F', '@val * 9/5 + 32')
;
Run Code Online (Sandbox Code Playgroud)
触发器触发并生成如下动态 sql 语句:
查询2:
SELECT 'ALTER FUNCTION dbo.fnConvert(@unit VARCHAR(3),@val DECIMAL(10,4))'+
' RETURNS TABLE AS'+
' RETURN SELECT CASE @unit'+
(SELECT ' WHEN '''+unit+''' THEN '+formula
FROM dbo.tblUnit
FOR XML PATH(''),TYPE).value('.','NVARCHAR(MAX)') +
' END val;'
Run Code Online (Sandbox Code Playgroud)
结果:
| COLUMN_0 |
|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| ALTER FUNCTION dbo.fnConvert(@unit VARCHAR(3),@val DECIMAL(10,4)) RETURNS TABLE AS RETURN SELECT CASE @unit WHEN 'KV' THEN @val * .865 WHEN 'KG' THEN @val / 2.205 WHEN 'BAR' THEN @val / 14.504 WHEN 'C' THEN @val * 9/5 + 32 END val; |
Run Code Online (Sandbox Code Playgroud)
然后执行该语句。该语句将函数替换为包含所有当前公式的版本。
现在我们可以在任何查询中使用该函数,如下所示:
查询3:
SELECT *
FROM dbo.tblRecords AS TR
JOIN dbo.tblUnit AS TU
ON TR.unit = TU.unit
CROSS APPLY dbo.fnConvert(TU.unit,TR.val) AS FC
Run Code Online (Sandbox Code Playgroud)
结果:
| ID | UNIT | VAL | CONVUNIT | FORMULA | CONVVAL |
|----|------|-----|----------|-----------------|-------------|
| 1 | KV | 3 | CV | @val * .865 | 2.595 |
| 1 | BAR | 4 | PSI | @val / 14.504 | 0.27578599 |
| 2 | KV | 7 | CV | @val * .865 | 6.055 |
| 2 | KG | 5 | lbs | @val / 2.205 | 2.267573696 |
| 3 | C | 37 | F | @val * 9/5 + 32 | 98.6 |
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
6041 次 |
最近记录: |