Using SQL to return a complete conversion table

Viewed 123

I have a table (UOMC) filled with Unit of Measure conversions. If you are familiar with JDE E1, this may look familiar. Each row has a conversion from one unit to another, along with a conversion to the "Primary Unit of Measure" or smallest unit of measure. [For example][1] [1]: https://i.stack.imgur.com/o4zfr.png

I want a SQL statement that will return a complete set of conversions, including to Miles from Feet without hard coding any name or value.

+-------+--------+------+------------+-----------------+
| To    | From   | Conv | PrimaryUOM | ConvToPrimary   |
+-------+--------+------+------------+-----------------+
| Feet  | Inches |   12 | Inches     |              12 |
| Yards | Feet   |    3 | Inches     |              36 |
| Miles | Yards  | 1760 | Inches     |           63360 |
+-------+--------+------+------------+-----------------+

CREATE TABLE [UOMC](
    [To] [nchar](10) NULL,
    [From] [nchar](10) NULL,
    [Conv] [int] NULL,
    [PrimaryUOM]  [nchar](10) NULL,
    [ConvToPrimary] [int] NULL


) ON [PRIMARY]
GO;

INSERT INTO [UOMC]
           ([To],[From],[Conv],[ConvToPrimary])
     VALUES
           ('Feet','Inches',12,'Inches',12),
           ('Yards','Feet',3,'Inches',36),
           ('Miles','Yards',1760,'Inches',63360)
GO;

The desired result...

+-------+--------+------+
| To    | From   | Conv |
+-------+--------+------+
| Feet  | Inches |   12 |
| Yards | Feet   |    3 |
| Miles | Yards  | 1760 |
| Miles | Feet   | 5280 |
+-------+--------+------+
3 Answers

use

select [From],[To],[Conv]
from UOMC
union all 
select [From],'Feet', Conv * 3 as Conv
from UOMC
where [To] = 'Miles'

demo in db<>fiddle

The following gives the correct results without hard coding any criteria into a WHERE clause.

drop TABLE if exists #UOMC;
go
CREATE TABLE #UOMC(
    [To] [nchar](10) NULL,
    [From] [nchar](10) NULL,
    [Conv] [int] NULL,
    [ConvToPrimary] [int] NULL
) ON [PRIMARY];

INSERT INTO #UOMC
           ([To], [From], [Conv],[ConvToPrimary])
     VALUES
           ('Feet','Inches',12,12),
           ('Yards','Feet',3,36),
           ('Miles','Yards',1760,63360);

select [To], [From], [Conv] from #UOMC
union all
select u2.[To], u1.[From], u1.Conv*u2.Conv
from #UOMC u1
     join #UOMC u2 on u1.[To]=u2.[From]
order by [To];
To          From        Conv
Feet        Inches      12
Miles       Yards       1760
Miles       Feet        5280
Yards       Inches      36
Yards       Feet        3

Personally, I'd go about it slightly differently.

For each unit in any given dimension, store its size in a common unit.

  • I'd use a common size in meters
  • For sake of copying your example, I've given everything in inches

Then, it's just a case of taking all combinations of units in the same dimension and to convert from A to B you simply multiply by the size of A and divide by the size of B.

For example...

Raw Data:

CREATE TABLE unit_dimension(
    id   INT IDENTITY(1,1) NOT NULL,
    name NVARCHAR(32)      NOT NULL,
);
INSERT INTO unit_dimension(name) VALUES ('distance'), ('time');

CREATE TABLE unit(
    id             INT IDENTITY(1,1) NOT NULL,
    name           NVARCHAR(32)      NOT NULL,
    plural         NVARCHAR(32)      NOT NULL,
    symbol         NVARCHAR(32)      NOT NULL,
    dimension_id   INT               NOT NULL,
    size           FLOAT             NOT NULL
);

INSERT INTO
  unit(name, plural, symbol, dimension_id, size)
VALUES
  (N'Inch',       N'Inches',      N'in', 1,                1.00),
  (N'Foot',       N'Feet',        N'ft', 1,               12.00),
  (N'Yard',       N'Yards',       N'yd', 1,               36.00),
  (N'Mile',       N'Miles',       N'mi', 1,            63360.00),
  (N'Meter',      N'Meters',       N'm', 1,   39.37007874015748),
  (N'Light Year', N'Light Years', N'ly', 1, 9460730472580800.00),
  (N'Angrstom',   N'Angstroms',    N'Å', 1,      3.937007874e-9)
;

Actual UOMC, as a view...

CREATE VIEW v_UOMC AS
  SELECT
    x.id              AS from_id,
    x.name            AS from_name,
    y.id              AS to_id,
    y.name            AS to_name,
    x.size / y.size   AS conversion_factor
  FROM
    unit   x
  INNER JOIN
    unit   y
      ON  x.dimension_id = y.dimension_id
;

Use thereof...

-- If I were to walk 5000 miles, how far would that be in meters?
SELECT
  5000 * conversion_factor
FROM
  v_UOMC
WHERE
      from_name = 'Mile'
  AND   to_name = 'Meter'
;

EDIT:

To do a little leg work for the OP, this is the same answer using their EXACT table (including corrections to their script)...

CREATE TABLE [UOMC](
    [From] [nchar](10) NULL,
    [To] [nchar](10) NULL,
    [Conv] [int] NULL,
    [PrimaryUOM]  [nchar](10) NULL,
    [ConvToPrimary] [int] NULL


)
;

INSERT INTO [UOMC]
           ([From],[To],[Conv],[PrimaryUOM],[ConvToPrimary])
     VALUES
           ('Feet','Inches',12,'Inches',12),
           ('Yards','Feet',3,'Inches',36),
           ('Miles','Yards',1760,'Inches',63360)
;

SELECT
  x.[From]                                  AS [From],
  y.[From]                                  AS [To],
  x.ConvToPrimary * 1.0 / y.ConvToPrimary   AS Conv,
  x.ConvToPrimary                           AS ConvToPrimary
FROM
  UOMC   x
INNER JOIN
  UOMC   y
    ON  x.PrimaryUOM = y.PrimaryUOM

EDIT:

Or even...

WITH
  UOMC_expanded AS
(
  SELECT * FROM UOMC
  UNION ALL
  SELECT PrimaryUOM, PrimaryUOM, 1, PrimaryUOM, 1 FROM UOMC GROUP BY PrimaryUOM
)
  SELECT
    x.[From]                                  AS [From],
    y.[From]                                  AS [To],
    x.ConvToPrimary * 1.0 / y.ConvToPrimary   AS Conv,
    x.PrimaryUOM                              AS PrimaryUOM,
    x.ConvToPrimary                           AS ConvToPrimary
  FROM
    UOMC_expanded   x
  INNER JOIN
    UOMC_expanded   y
      ON  x.PrimaryUOM = y.PrimaryUOM
  ORDER BY
    ConvToPrimary, Conv

Which gives...

From        To          Conv            PrimaryUOM  ConvToPrimary
Inches      Miles       0.000015782828  Inches      1
Inches      Yards       0.027777777777  Inches      1
Inches      Feet        0.083333333333  Inches      1
Inches      Inches      1.000000000000  Inches      1
Feet        Miles       0.000189393939  Inches      12
Feet        Yards       0.333333333333  Inches      12
Feet        Feet        1.000000000000  Inches      12
Feet        Inches      12.000000000000     Inches      12
Yards       Miles       0.000568181818  Inches      36
Yards       Yards       1.000000000000  Inches      36
Yards       Feet        3.000000000000  Inches      36
Yards       Inches      36.000000000000     Inches      36
Miles       Miles       1.000000000000  Inches      63360
Miles       Yards       1760.000000000000   Inches      63360
Miles       Feet        5280.000000000000   Inches      63360
Miles       Inches      63360.000000000000  Inches      63360
Related