我正在收集有关代码库的一些基本统计信息,并尝试使用以下架构数据生成查询
CREATE TABLE files (
id INT PRIMARY KEY,
path VARCHAR(255) NOT NULL UNIQUE,
region VARCHAR(4) CHECK (region IN ('NYK', 'LDN', 'CORE', 'TKY')),
)
CREATE TABLE file_stats (
date DATE NOT NULL,
file_id INT NOT NULL REFERENCES files,
num_lines INT NOT NULL,
CONSTRAINT file_stats__pk PRIMARY KEY(date, file_id)
)
我正在尝试创建一个查询,该查询将返回表中日期和区域的所有组合以及该组合的文件数。
的简单方法
SELECT date, region, COUNT(*) FROM file_stats fs, files f WHERE fs.file_id = f.id
GROUP BY date, region
不起作用,因为并非所有区域都在所有日期都被重新种植。 我试过了
SELECT
d.date,
r.region,
(SELECT COUNT(*) FROM file_stats fs, files f
WHERE fs.file_id = file.id AND fs.date = d.date AND d.region = r.region
) AS num_files
FROM
(SELECT DISTINCT date FROM file_stats) AS d,
(SELECT DiSTINCT region FROM files) AS r
但由于嵌套子查询,性能是不可接受的。
我尝试过LEFT OUTER JOINS,但似乎永远无法让它们发挥作用。 数据库是SQLITE
有人可以建议更好的查询吗?
答案 0 :(得分:0)
SELECT date, region, COUNT(*) FROM file_stats fs, files f WHERE fs.file_id = f.id
GROUP BY date, region
不起作用,因为并非所有地区都有 在所有日期都取消了。
假设你的意思是它可以正常工作,但是你需要所有的日期来显示一个区域是否会出现在那里,那么你需要两件事。
有了日历表之后,就像这样。 。
SELECT c.cal_date, f.region, COUNT(*)
FROM calendar c
LEFT JOIN file_stats fs ON (fs.date = c.cal_date)
INNER JOIN files f ON (fs.file_id = f.id)
GROUP BY date, region
我上面使用了cal_date。您使用的名称取决于您的日历表。这将帮助您入门。您可以使用电子表格生成日期。
CREATE TABLE calendar (cal_date date primary key);
INSERT INTO "calendar" VALUES('2011-01-01');
INSERT INTO "calendar" VALUES('2011-01-02');
INSERT INTO "calendar" VALUES('2011-01-03');
INSERT INTO "calendar" VALUES('2011-01-04');
INSERT INTO "calendar" VALUES('2011-01-05');
INSERT INTO "calendar" VALUES('2011-01-06');
INSERT INTO "calendar" VALUES('2011-01-07');
INSERT INTO "calendar" VALUES('2011-01-08');
如果您确定所有日期都在file_stats中,则可以不使用日历表。但有一些警告。
select fs.date, f.region, count(*)
from file_stats fs
left join files f on (f.id = fs.file_id)
group by fs.date, f.region;
如果您的数据是正确的,这将有效,但您的表格不保证数据是正确的。您没有外键引用,因此每个表中可能有文件ID号在另一个表中没有匹配的ID号。我们有一些样本数据。
insert into files values (1, 'a long path', 'NYK');
insert into files values (2, 'another long path', 'NYK');
insert into files values (3, 'a shorter long path', 'LDN'); -- not in file_stats
insert into file_stats values ('2011-01-01', 1, 35);
insert into file_stats values ('2011-01-02', 1, 37);
insert into file_stats values ('2011-01-01', 2, 40);
insert into file_stats values ('2011-01-01', 4, 35); -- not in files
运行此查询(与上面相同,但添加ORDER BY)。 。
select fs.date, f.region, count(*)
from file_stats fs
left join files f on (f.id = fs.file_id)
group by fs.date, f.region
order by fs.date, f.region;
。 。 。返回
2011-01-01||1
2011-01-01|NYK|2
2011-01-02|NYK|1
'LDN'没有显示,因为文件ID号为3的file_stats中没有行。一行有一个空区域,因为文件中的行没有文件ID号为4。
您可以使用左连接快速找到不匹配的行。
select f.id, fs.file_id
from files f
left join file_stats fs on (fs.file_id = f.id)
where fs.file_id is null;
返回
3|
意味着文件中有一行ID为3,但file_stats中的行没有id 3.翻转表以确定file_stats中文件中没有匹配行的行。
select fs.file_id, f.id
from file_stats fs
left join files f on (fs.file_id = f.id)
where f.id is null;
答案 1 :(得分:0)
我怀疑它必须为输出的每一行尝试扫描file_stats和文件。以下版本可能会快得多。并且它不需要创建新表。
SELECT d.date
, r.region
, count(f.file_id) AS num_files
FROM (SELECT DISTINCT date FROM file_states) AS d,
(SELECT DISTINCT region FROM files) AS r,
LEFT JOIN file_stats AS fs
ON fs.date = d.date
LEFT JOIN files f
ON f.file_id = fs.file_id
AND f.region = r.region
GROUP BY d.date, r.region;
答案 2 :(得分:0)
一个(由于下半年的性能影响较慢)做你想做的事的方式是一个UNION的东西,计数与制造的零计数的事情列表:
-- Include the counts for date/region pairs that HAVE files
SELECT date, region, COUNT(*) as COUNT1
FROM file_stats fs, files f
WHERE fs.file_id = f.id
GROUP BY date, region
UNION
SELECT DISTINCT date, region, 0 as COUNT1
FROM file_stats fs0, files f0
WHERE NOT EXISTS (
SELECT 1
FROM file_stats fs, files f
WHERE fs.file_id = f.id
AND fs.date=fs0.date
AND f.region=f0.region
)
我不完全确定你为什么反对使用临时表?例如。 (这是临时表填充的Sybasyish语法,但应该很容易移植 - 不要回想起确切的SQLite)。表大小应该是最小的(只是天数#*区域的数量)
CREATE TABLE COMBINATIONS TEMPORARY (region VARCHAR(4), date DATE)
INSERT COMBINATIONS SELECT DISTINCT date, region FROM files, file_stats
SELECT c.date, c.region, SUM(CASE WHEN file_stats.id IS NULL THEN 0 ELSE 1 END)
FROM COMBINATIONS c
LEFT JOIN files f ON f.region=c.region
LEFT OUTER JOIN file_stats fs ON fs.date=c.date AND fs.file_id = f.id
GROUP BY c.date, c.region