如何使这个SQL查询更有效,以及如何做更多

时间:2012-03-29 17:37:32

标签: sql ruby sqlite duplicates

我有一个sqlite3数据库,其中包含损坏的数据。我认定“腐败”具有以下特征:

如果出现以下情况,名称,电话,纬度,经度列中的数据都会损坏:值为NULL或“”或长度< 2

如果值为NULL或“”或单词数量,则地址列中的数据已损坏。 2和单词的长度是< 2

为了测试这个,我在Ruby中编写了以下脚本:

require 'sqlite3'

db = SQLite3::Database.new('development.sqlite3')

db.results_as_hash = true;

#Checks for empty strings in name, address, telephone, latitude, longitude
#Also checks length of strings is valid
rows = db.execute(" SELECT * FROM listings WHERE LENGTH('telephone') < 2 OR LENGTH('fax') < 2  OR LENGTH('address') < 2 OR LENGTH('city') < 2 OR LENGTH('province') < 2 OR LENGTH('postal_code') < 2 OR LENGTH('latitude') < 2 OR LENGTH('longitude') < 2 
OR name = '' OR address = '' OR telephone = '' OR latitude = '' OR longitude = '' ") 

rows.each do |row|
=begin
db.execute("INSERT INTO missing (id, name, telephone, fax, suite, address, city, province, postal_code, latitude, longitude, url) VALUES (?,?,?,?,?,?,?,?,?,?,?,?)", row['id'], row['name'], row['telephone'], row['fax'], row['suite'], row['address'], row['city'], row['province'],
row['postal_code'], row['latitude'], row['longitude'], row['url'] )
=end

  id_num = row['id']
  puts "Id = #{id_num}"

  corrupt_name = row['name']
  puts "name = #{corrupt_name}"

  corrupt_address = row['address']
  puts "address = #{corrupt_address}"

  corrupt_tel = row['telephone']
  puts "tel = #{corrupt_tel}"

  corrupt_lat = row['latitude']
  puts "lat = #{corrupt_lat}" 

  corrupt_long = row['longitude']
  puts "lat = #{corrupt_long}" 
  puts '===end===='

end
#After inserting the records into the new table delete them from the old table
=begin
db.execute(" DELETE * FROM listings WHERE LENGTH('telephone') < 2 OR LENGTH('fax') < 2  OR LENGTH('address') < 2 OR 
LENGTH('city') < 2 OR LENGTH('province') < 2 OR LENGTH('postal_code') < 2 OR LENGTH('latitude') < 2 OR LENGTH('longitude') < 2 
OR name = '' OR address = '' OR telephone = '' OR latitude = '' OR longitude = '' ")
=end

这可行,但我是Ruby和DB编程的新手。所以我欢迎任何建议,以使这个查询更好。 我的最终目标是在我的数据库上运行一个脚本来测试其中数据的有效性,如果有些数据无效,它们将被复制到另一个表并从第一个表中删除。

另外,我想在此查询中添加一个测试来检查重复的条目。

如果超过1行共享相同的名称,相同的地址和相同的电话以及相同的纬度和相同的经度,我将条目限定为重复

我提出了这个问题,但我不确定它是否是最优的:

SELECT * 
FROM listings L1, listings L2
WHERE L1.name = L2.name
AND L1.telephone = L2.telephone
AND L1.address = L2.address
AND L1.latitude = L2.latitude
AND L1.longitude = L2.longitude

任何建议,链接,帮助将不胜感激

2 个答案:

答案 0 :(得分:2)

您的第一个查询没有任何重大的性能问题。它将使用seq扫描运行,评估您的“已损坏”谓词。对== ''的{​​{1}}检查是多余的,因为长度('')是&lt; 2.您有一个错误,您在length()调用中引用了字段名称,因此您将评估文字字段名称的长度而不是字段的值。您也无法测试NULL,这是一个与''不同的值。您可以使用length(foo) < 2函数将NULL转换为''并使用长度检查捕获NULLS。您似乎也没有解决基于特殊字的地址规则。除非使用regexp函数扩展sqlite,否则以后会出现问题。我建议用LIKE或GLOB近似它。

尝试这个替代方案:

coalesce

您发现重复查询不起作用,因为在自相连时总是至少有一条记录要匹配。您需要在联接的一侧排除测试中的记录。通常,这可以通过排除主键来完成。您没有提到该表是否具有主键,但IIRC sqllite可以为您提供具有ROWID的代理。像这样:

SELECT * FROM listings
WHERE LENGTH(coalesce(telephone,'')) < 2
OR LENGTH(coalesce(fax,'')) < 2 
OR LENGTH(coalesce(city,'')) < 2 
OR LENGTH(coalesce(province,'')) < 2 
OR LENGTH(coalesce(postal_code,'')) < 2 
OR LENGTH(coalesce(latitude,'')) < 2 
OR LENGTH(coalesce(longitude,'')) < 2 
OR LENGTH(coalesce(name,'')) < 2
OR LENGTH(coalesce(address,'')) < 5
OR trim(address) not like '%__ __%'
顺便说一句,虽然你强调了问题的效率,但在担心效率之前让代码正确是很重要的。

答案 1 :(得分:0)

我认为你正在进行过度处理。由于字符串''的长度为0,因此它与condicion length('') < 2匹配。因此,您不需要检查字段是否等于'',因为它已经被长度函数的条件过滤了。

但是,我不知道你是如何检查空值的。我将所有aField = ''替换为aField is null