使用SQL进行帐单计算

时间:2014-07-23 19:34:11

标签: sql database oracle

我在尝试计算消费总额时遇到了一些问题。 例如: 对于前20瓦特,收费为0.40 下一个20瓦的电量为0.80 超过20瓦的每瓦特0.90和0.90

我已尝试自己做某事但计算错误,有没有办法优化查询,因为我不希望在主select语句中进行计算。子查询是一个更好的选择吗?

我使用Oracle作为数据库引擎

SELECT CustomerName, previousRead, newRead, newRead-previousRead AS Consumption, 
(((newRead-previousRead-100)*FR)+((newRead-previousRead-200)*SR)+((newRead-previousRead-200)/200)*TR)as TotalBill 
  FROM (
        SELECT C.firstName||''||C.lastName as CustomerName, R.newReading previousRead,
               lead(R.newReading) OVER (PARTITION BY R.meterID ORDER BY R.dateVisited) AS newRead, B.firstconsumptionRate as FR,
               B.secondconsumptionRate as SR, B.firstconsumptionRate as TR
        FROM reading R, serviceaddress S, electricmeter W, customer C, rate A, nonresidentialrate B
           WHERE W.meterID = S.meterID
           AND R.meterID = W.meterID
           AND A.rateID = S.rateID
           AND B.rateID = A.rateID
           AND C.custID = S.custID
           AND C.custType = 'Non-residential'
           AND  r.datevisited >=  TO_DATE('01-06-2014','DD-MM-YYYY') 
           AND r.datevisited < TO_DATE('31-07-2014','DD-MM-YYYY')
        )where newRead is not null;

CREATE TABLE Customer(
custID              INTEGER NOT NULL,
firstName           CHAR(25) NOT NULL,
lastName            CHAR(25) NOT NULL,
NRIC                CHAR(9) NOT NULL,
custType            CHAR(25) NOT NULL,
badStatus           CHAR(25) Default 'Good',
CONSTRAINT cust_Pkey PRIMARY KEY (custID),
CONSTRAINT cust_type CHECK (custType IN ('Residential', 'Non-residential')),
CONSTRAINT custBadStatus_type CHECK (badStatus IN ('Late Payment', 'Non Payment', 'Good'))
);

CREATE TABLE Reading (
readingID       INTEGER NOT NULL,
meterID         INTEGER NOT NULL,
dateVisited     DATE NOT NULL, 
newReading      NUMBER(10,0) NOT NULL,
CONSTRAINT reading_Pkey PRIMARY KEY (readingID),
CONSTRAINT reading_AltKey UNIQUE (meterID, dateVisited),
CONSTRAINT reading_meterID_Fkey FOREIGN KEY (meterID) REFERENCES Meter (meterID),
CONSTRAINT checkReading CHECK (newReading > 0)
);

CREATE TABLE Rate (
rateID          INTEGER NOT NULL,
rateApprDate    DATE NOT NULL,
rateEffDate     DATE NOT NULL,
rateType        CHAR(25) NOT NULL,
CONSTRAINT rate_Pkey PRIMARY KEY (rateID),
CONSTRAINT rateType CHECK (rateType IN ('Residential', 'Non-residential')),
CONSTRAINT validDate CHECK (rateApprDate < rateEffDate),
);

CREATE TABLE NonResidentialRate (
rateID                      INTEGER NOT NULL,
firstconsumptionRate        NUMBER(5,2) NOT NULL,
secondconsumptionRate       NUMBER(5,2) NOT NULL,
thirdconsumptionRate        NUMBER(5,2) NOT NULL,
CONSTRAINT nonResidentialRate_Pkey PRIMARY KEY (rateID),
CONSTRAINT nonResidentialRate_rateID_FK FOREIGN KEY (rateID) REFERENCES Rate (rateID)
);

CREATE TABLE ServiceAddress (
svcAddID            INTEGER NOT NULL,
meterID             INTEGER NOT NULL,
custID              INTEGER NOT NULL,
rateID              INTEGER NOT NULL,
street              CHAR(30) NOT NULL,
city                CHAR(35) NULL,
state               CHAR(2) NULL,
zipPostalCode       CHAR(9) NOT NULL, 
CONSTRAINT serviceAddress_Pkey PRIMARY KEY (svcAddID),
CONSTRAINT serviceAddress_meterID_Fkey FOREIGN KEY (meterID) REFERENCES Meter (meterID),
CONSTRAINT serviceAddress_custID_Fkey FOREIGN KEY (custID) REFERENCES Customer (custID),
CONSTRAINT serviceAddress_rateID_Fkey FOREIGN KEY (rateID) REFERENCES Rate (rateID),
);

CREATE TABLE Meter (
meterID             INTEGER NOT NULL,
SerialNum           CHAR(30) NOT NULL,
installationDate    DATE NOT NULL,
CONSTRAINT waterMeter_Pkey PRIMARY KEY (meterID),
CONSTRAINT waterMeter_Altkey UNIQUE (SerialNum)
);

客户既可以是住宅也可以是非住宅,每个都有服务地址。仪表对每个服务地址都是唯一的,读数存储在不同的表中。 有2种费率,他们是住宅和非住宅的不同种类的费率。

至于阅读,我没有为以前的阅读创建一个新属性,因为它可以在上个月阅读中获得

希望这会让我的问题更加清晰。

2 个答案:

答案 0 :(得分:0)

如果是我,我可能会使用CASE语句并为了清晰起见而构建代码,而不是试图剔除每个性能的最后一个周期(除非你真的在一个环境中你需要每个最后一个周期)

(CASE WHEN newRead-previousRead <= 20
      THEN (newRead-previousRead)*0.40
      WHEN newRead-previousRead <= 40
      THEN 20*0.40 + (newRead-previousRead-20)*0.80
      WHEN newRead-previousRead > 40
      THEN 20*0.40 + 20*0.80 + (newRead-previousRead-40)*0.90
  END)

现在,显然,有一些方法可以对此进行优化(例如,通过在内部查询中计算newRead-previousRead一次,而不是可能需要在CASE语句中计算它4次)。如果您希望能够频繁更改结算结构,则这不是一个特别灵活的选项(在这种情况下,层和速率需要在您​​阅读的单独表中)。但它有望成为一种相对清晰的计算表达方式。

答案 1 :(得分:0)

你去,你可以尝试一下..

SELECT CustomerName, previousRead, newRead, newRead-previousRead AS Consumption, 
    CASE WHEN newRead-previousRead <= 100
          THEN (newRead-previousRead)*FR
          WHEN newRead-previousRead <= 200
          THEN 100*FR + (newRead-previousRead-100)*SR
          WHEN newRead-previousRead > 200
          THEN 100*FR + 100*SR + ((newRead-previousRead)-200)*TR 
      END as TotalBill
      FROM (
            SELECT C.firstName||''||C.lastName as CustomerName, R.newReading previousRead,
                   lead(R.newReading) OVER (PARTITION BY R.meterID ORDER BY R.dateVisited) AS newRead, B.firstconsumptionRate as FR,
                   B.secondconsumptionRate as SR, B.thirdconsumptionRate as TR
            FROM reading R, serviceaddress S, meter W, customer C, rate A, nonresidentialrate B
               WHERE W.meterID = S.meterID
               AND R.meterID = W.meterID
               AND A.rateID = S.rateID
               AND B.rateID = A.rateID
               AND C.custID = S.custID
               AND C.custType = 'Non-residential'
               AND  r.datevisited >=  TO_DATE('01-06-2014','DD-MM-YYYY') 
               AND r.datevisited < TO_DATE('31-07-2014','DD-MM-YYYY')
            )where newRead is not null;