SQL中的单元转换表

时间:2012-06-21 17:49:34

标签: sql database sql-server-2008

我想在数据库中实现单位转换功能,用于质量,体积,温度等单位。我的背景是软件开发,所以我自然倾向于为此任务创建一堆用户定义函数。但是我怀疑有更多的“SQLish”方式来实现这一目标。

基于表格的方法,例如herehere所描述的方法非常好,但我认为没有简单的方法可以为单个乘法器无关的单元扩展此方法,例如温度(F-to-C,C-to-F)或原子质量(kgmol-to-kg)。我已经看到了多步骤方法(例如描述here的方法),但这些方法似乎太复杂而无用。

我在这里遗漏了一些明显的东西,还是功能真的是唯一的出路?

3 个答案:

答案 0 :(得分:1)

要处理温度转换,转换表应具有乘数和偏移量。对于F - >例如,C的偏移量为-32,乘数为5/9。

如果您事先知道所有可能的单位,那么基于表格的消息可以正常工作。但是,如果您想要一个完全灵活的系统,例如米^ 5 *升到英寸^ 5 *加仑,那么您将需要一个基本单位表和一个用户定义的函数来进行转换。此函数可能使用递归cte来解析单位表达式。所有这些都会相当复杂,所以希望你有一份完整的单位清单。

答案 1 :(得分:0)

最后,我决定坚持使用UDF。该决定的主要动机是,基于表格的方法的复杂性似乎并不合理,因为这项任务的频率很低。此外,若干函数(例如ConvertMass())依赖于子查询(例如,确定给定组件的原子量)。所以UDF似乎是最谨慎的方式。

答案 2 :(得分:0)

自从提出这个问题以来已经过了几年,但是我创建了一个解决方案,这个解决方案今天对于这个主题可能具有未来的兴趣。在SQL Server 2012上测试了以下解决方案。为了减少页面上的代码大小,我只提供质量测量。

CREATE TABLE [Measurement type]
(
   [Type ID] INT IDENTITY(1,1) PRIMARY KEY NOT NULL,
   [Type Name] NVARCHAR(30) NOT NULL
)
GO
CREATE TABLE [Measurement unit]
(
   [Unit ID] INT IDENTITY(1,1) PRIMARY KEY NOT NULL,
   [Type ID] INT REFERENCES [Measurement type]([Type ID]) NOT NULL,
   [Unit name] NVARCHAR(30) NOT NULL,
   [Unit symbol] NVARCHAR(10) NOT NULL
)
GO
/* Use both multiplicand and dividend to reduce rounding errors */
CREATE TABLE [Measurement conversions]
(
   [Type ID] INT NOT NULL REFERENCES [Measurement type]([Type ID]),
   [From Unit ID] INT NOT NULL REFERENCES [Measurement unit]([Unit ID]),
   [To Unit ID] INT NOT NULL REFERENCES [Measurement unit]([Unit ID]),
   [From Unit Offset] FLOAT NOT NULL DEFAULT(0),
   [Multiplicand] FLOAT NOT NULL DEFAULT(1),
   [Dividend] FLOAT NOT NULL DEFAULT(1),
   [To Unit Offset] FLOAT NOT NULL DEFAULT(0),
   PRIMARY KEY ([Type ID], [From Unit ID], [To Unit ID])
)
GO
SET IDENTITY_INSERT [Measurement type] ON
GO
INSERT INTO [Measurement type]([Type ID], [Type Name])
VALUES (1, 'Length'), (2, 'Area'), (3, 'Volume'), (4, 'Mass'), (5, 'Temperature') -- ...
GO
SET IDENTITY_INSERT [Measurement type] OFF
GO
GO
SET IDENTITY_INSERT [Measurement unit] ON
GO
INSERT INTO [Measurement unit]([Unit ID], [Type ID], [Unit name], [Unit symbol])
VALUES (28, 4, 'Milligram', 'mg'), (29, 4, 'Gram', 'g'),
       (30, 4, 'Kilogram', 'kg'), (31, 4, 'Tonne', 't'),
       (32, 4, 'Ounce', 'oz'), (33, 4, 'Pound', 'lb'),
       (34, 4, 'Stone', 's'), (35, 4, 'hundred weight', 'cwt'),
       (36, 4, 'UK long ton', 'ton')
GO
SET IDENTITY_INSERT [Measurement unit] ON
GO
INSERT INTO [Measurement conversions]([Type ID], [From Unit ID], [To Unit ID], [Multiplicand], [Dividend])
VALUES (4, 28, 29, 1, 1000), (4, 28, 30, 1, 1000000), (4, 28, 31, 1, 1000000000),
       (4, 28, 32, 1, 28350), (4, 32, 33, 1, 16), (4, 32, 34, 1, 224),
       (4, 32, 35, 1, 50802345), (4, 32, 36, 1, 35840)
GO
INSERT INTO [Measurement conversions]([Type ID], [From Unit ID], [To Unit ID], [From Unit Offset], [Multiplicand], [Dividend], [To Unit Offset])
SELECT DISTINCT [Measurement Conversions].[Type ID],
                [Measurement Conversions].[To Unit ID],
                [Measurement Conversions].[From Unit ID],
                -[Measurement Conversions].[To Unit Offset],
                [Measurement Conversions].[Dividend],
                [Measurement Conversions].[Multiplicand],
                -[Measurement Conversions].[From Unit Offset]
FROM [Measurement Conversions]
-- LEFT JOIN Used to reduce errors on inserting same key twice.
LEFT JOIN [Measurement conversions] AS [Existing]
ON [Measurement Conversions].[From Unit ID] = [Existing].[To Unit ID] AND [Measurement Conversions].[To Unit ID] = [Existing].[From Unit ID]
WHERE [Existing].[Type ID] IS NULL
GO

然后运行以下查询直到它影响零行,如果有多个路径导致相同值之间的转换因子不同,则可能会抱怨多个等号ID。

INSERT INTO [Measurement conversions]([Type ID], [From Unit ID], [To Unit ID], [From Unit Offset], [Multiplicand], [Dividend], [To Unit Offset])
SELECT DISTINCT [From].[Type ID],
                [From].[To Unit ID] AS [From Unit ID],
                [To].[To Unit ID],
                -[From].[To Unit Offset] + (([To].[From Unit Offset]) * [From].[Multiplicand] / [From].Dividend) AS [From Unit Offset],
                [From].[Dividend] * [To].[Multiplicand] AS Multiplicand,
                [From].[Multiplicand] * [To].[Dividend] AS Dividend,
                [To].[To Unit Offset] - (([From].[From Unit Offset]) * [To].[Multiplicand] / [To].Dividend) AS [To Unit Offset]
FROM [Measurement conversions] AS [From]
CROSS JOIN [Measurement conversions] AS [To]
-- LEFT JOIN Used to reduce errors on inserting same key twice.
LEFT JOIN [Measurement conversions] AS [Existing]
ON [From].[To Unit ID] = [Existing].[From Unit ID] AND [To].[To Unit ID] = [Existing].[To Unit ID]
WHERE [Existing].[Type ID] IS NULL
      AND [From].[Type ID] = [To].[Type ID]
      AND [From].[To Unit ID] <> [To].[To Unit ID]
      AND [From].[From Unit ID] = [To].[From Unit ID]

最后,删除直接取消彼此的乘数和分数:

UPDATE [Measurement conversions] SET [Multiplicand] = 1, [Dividend] = 1 WHERE Multiplicand = Dividend