Следующее решение протестировано на SQL Server 2012. Чтобы уменьшить размер кода на странице, я предоставляю только измерения массы, поскольку они проверены и работают.
CREATE TABLE [Measurement type]
(
[Type ID] INT IDENTITY(1,1) PRIMARY KEY NOT NULL,
[Type Name] NVARCHAR(30) NOT NULL
)
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
)
/* Use both multiplier and divizor 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),
[Multiplier] FLOAT NOT NULL DEFAULT(1),
[Divizor] FLOAT NOT NULL DEFAULT(1),
[To Unit Offset] FLOAT NOT NULL DEFAULT(0),
PRIMARY KEY ([Type ID], [From Unit ID], [To Unit ID])
)
INSERT INTO [Measurement type]([Type ID], [Type Name]) VALUES(4, 'Mass')
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')
INSERT INTO [Measurement conversions]([Type ID], [From Unit ID], [To Unit ID], [Multiplier], [Divizor])
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)
INSERT INTO [Measurement conversions]([Type ID], [From Unit ID], [To Unit ID], [From Unit Offset], [Multiplier], [Divizor], [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].[Divizor],
[Measurement Conversions].[Multiplier],
-[Measurement Conversions].[From Unit Offset]
FROM [Measurement Conversions]
-- LEFT JOIN Used to assure that we dont try to insert already existing keys.
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
Запускайте следующий запрос, пока он не затронет 0 строк.
INSERT INTO [Measurement conversions]([Type ID], [From Unit ID], [To Unit ID], [From Unit Offset], [Multiplier], [Divizor], [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].[Multiplier] / [From].Divizor) AS [From Unit Offset],
[From].[Divizor] * [To].[Multiplier] AS Multiplier,
[From].[Multiplier] * [To].[Divizor] AS Divizor,
[To].[To Unit Offset] - (([From].[From Unit Offset]) * [To].[Multiplier] / [To].Divizor) AS [To Unit Offset]
FROM [Measurement conversions] AS [From]
CROSS JOIN [Measurement conversions] AS [To]
-- LEFT JOIN Used to assure that we dont try to insert already existing keys.
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]