高效的SQLite3声明

时间:2015-03-12 17:33:40

标签: database sqlite

我希望在SQLite3的另一个查询中使用查询结果。例如,请考虑SQLite数据库中的下表。

No   Name   Value1  Value2  Value3  Salary
------------------------------------------
1   Kid1     100     0       0       300
2   Kid2     200     700     0       400

可以使用以下代码创建上表。非常感谢Mike Sherrill和Cat Recall分享这段代码。

CREATE TABLE test (
  no integer primary key,
  name varchar(15) not null,
  value1 integer not null,
  value2 integer not null,
  value3 integer not null,
  salary decimal(13, 2) not null
);
INSERT INTO "test" VALUES(1,'Kid1',100,0,0,300);
INSERT INTO "test" VALUES(2,'Kid2',200,700,0,400);

现在,我需要使用以下规则更新SQLite3表。

对于每一行,更新Salary = Salary + Value1/N,其中N表示行中(值2和值3)中零值的数量。

在上面的例子中,执行命令后我想得到下表

No   Name   Value1  Value2  Value3  Salary
------------------------------------------
1   Kid1     100     0       0       350  
2   Kid2     200     700     0       600

1 个答案:

答案 0 :(得分:1)

将CREATE TABLE和INSERT语句粘贴到您的问题中以获得更多答案。很多人不会花时间对数据库结构进行逆向工程。 (对于SQLite,请使用.dump your-table-name。)

要回答您的问题,我认为了解如何执行此操作非常重要。我也认为不要这样做很重要。请参阅下面水平线规则下面的文字和代码。

CREATE TABLE test (
  no integer primary key,
  name varchar(15) not null,
  value1 integer not null,
  value2 integer not null,
  value3 integer not null,
  salary decimal(13, 2) not null
);
INSERT INTO "test" VALUES(1,'Kid1',100,0,0,300);
INSERT INTO "test" VALUES(2,'Kid2',200,700,0,400);

此SELECT语句为我们提供每行中的零个数。我们不关心零出现在哪一列(value2或value3)。

select no, case when value2 = 0 then 1 else 0 end as num_zeroes from test
union all
select no, case when value3 = 0 then 1 else 0 end from test;
no          num_zeroes
----------  ----------
1           1         
2           0         
1           1         
2           1         

以下陈述总结了我们的零数。

select no, sum(num_zeroes) as num_zeroes
from (select no, case when value2 = 0 then 1 else 0 end as num_zeroes from test
      union all
      select no, case when value3 = 0 then 1 else 0 end from test) as zeroes
group by no;
no          num_zeroes
----------  ----------
1           2         
2           1        

我认为这个结果非常有用,可以从中获得一个视图。

create view num_zeroes as 
select no, sum(num_zeroes) as num_zeroes
from (select no, case when value2 = 0 then 1 else 0 end as num_zeroes from test
      union all
      select no, case when value3 = 0 then 1 else 0 end from test) as zeroes
group by no;

现在我们可以使用连接和一个小算术进行计算。这个SELECT语句显示了这些部分是如何工作的。

select 
  t1.no, t1.value1, t1.salary, 
  t2.num_zeroes, t1.salary + (t1.value1/t2.num_zeroes) as new_salary
from test t1
inner join num_zeroes t2 on t1.no = t2.no;

这个更新。

update test
set salary = (select t1.salary + (t1.value1/t2.num_zeroes) 
              from test t1 
              inner join num_zeroes t2 on t1.no = t2.no 
              where t1.no = test.no);

<小时/> 这种方法的问题是某人可能多次运行更新。如果发生这种情况,可能很难恢复原始值。

为薪资历史创建一个表格。当您使用薪水历史表时,更新错误更容易解决。

我使用日期'9999-12-31'来表示“时间的结束”。

pragma foreign_keys = on;

create table salary_history (
  no integer not null,
  start_date date not null,
  end_date date not null default '9999-12-31',
  salary decimal(13, 2) not null,
  foreign key (no) references test (no),
  primary key (no, start_date)
);

插入几行以启动我们。我凭空掏出'2015-01-01'。我不知道你的原始表中value1,value2和value3的含义是什么,所以我不知道它们是否应该被移入“salary_history”。我让他们独自一人。

insert into salary_history values
(1, '2015-01-01', '9999-12-31', 300),
(2, '2015-01-01', '9999-12-31', 400);

我认为目前工资的观点通常是一个好主意,它使以后的计算更容易。

create view current_salary as
select no, start_date, end_date, salary
from salary_history
where current_date >= start_date 
  and current_date < end_date;

要更新薪水历史记录,请围绕两个SQL UPDATE语句包装事务。第一个通过将结束日期设置为当前日期来终止当前工资。第二个插入一个新行,它将成为当前的工资。

begin transaction;

update salary_history
set end_date = current_date
where end_date = '9999-12-31';

insert into salary_history
select 
  t1.no, current_date, '9999-12-31', 
  t1.salary + (t3.value1/t2.num_zeroes) as new_salary
from current_salary as t1
inner join num_zeroes as t2 on t1.no = t2.no
inner join test as t3 on t1.no = t3.no;

commit;