我在尝试计算消费总额时遇到了一些问题。 例如: 对于前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种费率,他们是住宅和非住宅的不同种类的费率。
至于阅读,我没有为以前的阅读创建一个新属性,因为它可以在上个月阅读中获得
希望这会让我的问题更加清晰。
答案 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;