Расчет поля SQL Server на основе нескольких условий - PullRequest
1 голос
/ 13 сентября 2011

Вот мой сценарий:

У меня есть таблица Person со следующими полями.

create table Person(PersonID int primary key identity(1,1),
                     Age int,
                     height decimal(4,2),
                     weight decimal(6,2)
                    );

 insert into Person(Age,height,weight) values (60,6.2,169); -- 1
 insert into Person(Age,height,weight) values (15,5.1,100); -- 2
 insert into Person(Age,height,weight) values (10,4.5,50); -- 3

Что мне нужно сделать, это

if the person Age >= 18 and height >= 6 then calculationValue = 20
if the person Age >= 18 and height < 6 then calculationValue = 15
if the person Age < 18 and weight >= 60 then calculationValue = 10
if the person Age < 18 and weight < 60 then calculationValue = 5

на основеЭтим условием мне нужно найти значение для вычисления и выполнить некоторые математические вычисления.

Я попытался создать гибкую модель, чтобы в будущем было проще добавить дополнительные условия и легко изменить постоянные значения (например, 18, 6)., 60 и т. Д.)

Я создал пару таблиц, как показано ниже:

create table condTable(condTableID int primary key identity(1,1),
                        condCol varchar(20),
                        startValue int,
                        endValue int
                      );

  insert into condTable(condCol,startValue,endValue) values ('Age',18,999) -- 1
  insert into condTable(condCol,startValue,endValue) values ('Height',6,99) -- 2
  insert into condTable(condCol,startValue,endValue) values ('Height',0,5.99) -- 3
  insert into condTable(condCol,startValue,endValue) values ('Age',0,17) -- 4
  insert into condTable(condCol,startValue,endValue) values ('Weight',60,999) -- 5
  insert into condTable(condCol,startValue,endValue) values ('Weight',0,59) -- 6

Я соединяю два условия, чтобы сделать его одним в следующей таблице, как указано в требовании (т.е. если возраст> = 18 и высота> = 6, тогда значение расчета = 20. и т. Д.)

create table CondJoin(CondJoin int,condTableID int,CalculationValue int)


insert into CondJoin values (1,1,20)
insert into CondJoin values (1,2,20)
insert into CondJoin values (2,1,15)
insert into CondJoin values (2,3,15)
insert into CondJoin values (3,4,10)
insert into CondJoin values (3,5,10)
insert into CondJoin values (4,4,5)
insert into CondJoin values (4,6,5)

Я думаю, что эта модель обеспечит гибкость добавления большего количества условий в будущем.Но у меня возникают трудности при реализации этого в SQL Server 2005. Любой может написать sql, который обрабатывает в заданной основе, сравнить значение в таблице Person с таблицей CondJoin и предоставить соответствующее значение вычисления.Например,для человека с идентификатором 1 следует взглянуть на таблицу CondJoin и указать значение для вычисления 20, так как его возраст превышает 18 лет, а рост превышает 6.

Ответы [ 3 ]

0 голосов
/ 13 сентября 2011

Ниже приведено крайне грубо, но оно должно понять смысл.Он нормализует данные и движется к полуобъектно-ориентированной структуре (атрибут / значение / значение атрибута).Я оставлю это на ваше усмотрение, чтобы укрепить ссылочную целостность, но следующее является гибким и даст желаемые результаты:

CREATE TABLE Person (
    PersonID INT PRIMARY KEY IDENTITY(1,1)
    ,Name NVARCHAR(255)
    );

GO

CREATE TABLE PersonAttribute (
    PersonID INT
    ,CondAttributeID INT
    ,Value NVARCHAR(255)
    );

GO

CREATE TABLE CondAttribute (
AttributeID INT PRIMARY KEY IDENTITY(1,1)
,Attribute NVARCHAR(255));

GO

CREATE TABLE CondTable (
    CondTableID INT PRIMARY KEY IDENTITY(1,1)
    ,CondAttributeID INT
    ,StartValue MONEY
    ,EndValue MONEY
    );

GO

CREATE TABLE CalculationValues (
    CalculationID INT PRIMARY KEY IDENTITY(1,1)
    ,CalculationValue INT
    );

GO

CREATE TABLE CondCalculation (
    CondTableID INT
    ,CalculationID INT
    );


INSERT Person (Name)

VALUES ('Joe')
        ,('Bob')
        ,('Tom');


INSERT PersonAttribute (
    PersonID
    ,CondAttributeID
    ,Value
)

VALUES  (1, 1, '60')
        ,(1, 2, '6.2')
        ,(1, 3, '169')
        ,(2, 1, '15')
        ,(2, 2, '5.1')
        ,(2, 3, '100')
        ,(3, 1, '10')
        ,(3, 2, '4.5')
        ,(3, 3, '50');          

INSERT CondAttribute (Attribute)

VALUES ('Age')
        ,('height')
        ,('weight');

INSERT CondTable (
    CondAttributeID
    ,StartValue
    ,EndValue)

VALUES (1,18,999) --Age
        ,(2,6,99) --Height 
        ,(2,0,5.99) -- Height
        ,(1,0,17) -- Age
        ,(3,60,999) -- Weight
        ,(3,0,59); -- Weight



INSERT CalculationValues (CalculationValue)
VALUES (5)
        ,(10)
        ,(15)
        ,(20);  


INSERT CondCalculation (CondTableID, CalculationID)
VALUES (1,4)
       ,(2,4)
       ,(1,3)
       ,(3,3)
       ,(4,2)
       ,(5,2)
       ,(5,1)
       ,(6,1);

SELECT *
FROM Person AS p
JOIN PersonAttribute AS pa ON p.PersonID = pa.PersonID
JOIN CondAttribute AS ca ON pa.CondAttributeID = ca.AttributeID
JOIN CondTable AS ct ON ca.AttributeID = ct.CondAttributeID
    AND CONVERT(money,pa.Value) BETWEEN ct.StartValue AND ct.EndValue
JOIN CondCalculation AS cc ON cc.CondTableID = ct.CondTableID
JOIN CalculationValues AS c ON cc.CalculationID = c.CalculationID
WHERE p.PersonID = 1
0 голосов
/ 13 сентября 2011

Следующее решение использует PIVOT (дважды) для преобразования комбинации CondJoin и condTable в диаграмму, а затем присоединяет диаграмму к таблице Person для вычисления целевого значения.Я полагаю, что вместо этого можно использовать и серию выражений CASE.Во всяком случае ...

Все таблицы были преобразованы в переменные таблицы, чтобы упростить тестирование.Итак, сначала DDL и подготовка данных:

declare @Person table(PersonID int primary key identity(1,1),
                     Age int,
                     height decimal(4,2),
                     weight decimal(6,2)
                    );
insert into @Person(Age,height,weight) values (60,6.2,169); -- 1
insert into @Person(Age,height,weight) values (15,5.1,100); -- 2
insert into @Person(Age,height,weight) values (10,4.5,50); -- 3

declare @condTable table(condTableID int primary key identity(1,1),
                        condCol varchar(20),
                        startValue int,
                        endValue int
                      );
insert into @condTable(condCol,startValue,endValue) values ('Age',18,999) -- 1
insert into @condTable(condCol,startValue,endValue) values ('Height',6,99) -- 2
insert into @condTable(condCol,startValue,endValue) values ('Height',0,5.99) -- 3
insert into @condTable(condCol,startValue,endValue) values ('Age',0,17) -- 4
insert into @condTable(condCol,startValue,endValue) values ('Weight',60,999) -- 5
insert into @condTable(condCol,startValue,endValue) values ('Weight',0,59) -- 6
declare @CondJoin table(CondJoin int,condTableID int,CalculationValue int);
insert into @CondJoin values (1,1,20)
insert into @CondJoin values (1,2,20)
insert into @CondJoin values (2,1,15)
insert into @CondJoin values (2,3,15)
insert into @CondJoin values (3,4,10)
insert into @CondJoin values (3,5,10)
insert into @CondJoin values (4,4,5)
insert into @CondJoin values (4,6,5)

А теперь запрос:

;with startValues as (
  select
    CondJoin,
    Age,
    Height,
    Weight,
    CalculationValue
  from (
    select
      j.CondJoin,
      j.CalculationValue,
      t.condCol,
      t.startValue
    from @CondJoin j
      inner join @condTable t on j.condTableID = t.condTableID
  ) j
  pivot (
    max(startValue) for condCol in (Age, Height, Weight)
  ) p
),
endValues as (
  select
    CondJoin,
    Age,
    Height,
    Weight,
    CalculationValue
  from (
    select
      j.CondJoin,
      j.CalculationValue,
      t.condCol,
      t.endValue
    from @CondJoin j
      inner join @condTable t on j.condTableID = t.condTableID
  ) j
  pivot (
    max(endValue) for condCol in (Age, Height, Weight)
  ) p
),
combinedChart as (
select
  s.CondJoin,
  AgeFrom    = s.Age,
  AgeTo      = e.Age,
  HeightFrom = s.Height,
  HeightTo   = e.Height,
  WeightFrom = s.Weight,
  WeightTo   = e.Weight,
  s.CalculationValue
from startValues s
  inner join endValues e on s.CondJoin = e.CondJoin
)
select
  p.*,
  c.CalculationValue
from @Person p
  left join combinedChart c
    on (c.AgeFrom    is null or p.Age    between c.AgeFrom    and c.AgeTo)
   and (c.HeightFrom is null or p.Height between c.HeightFrom and c.HeightTo)
   and (c.WeightFrom is null or p.Weight between c.WeightFrom and c.WeightTo)
0 голосов
/ 13 сентября 2011

похоже, вы движетесь к динамической генерации SQL.

я думаю, что, возможно, вам лучше иметь строку для каждого столбца и значения отсечения для диапазонов, а также значение, если оно истинно ... может быть что-то вроде:

age_condition
-----------------
min_age
max_age
value

это то, что вы можете заполнить, а затем запросить без какой-либо динамической генерации.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...