Как сделать этот SQL-запрос более эффективным и как сделать больше - PullRequest
0 голосов
/ 29 марта 2012

У меня есть база данных sqlite3, в которой у меня повреждены данные. Я квалифицируюсь как «коррумпированный» со следующими характеристиками:

Данные в столбцах имени, телефона, широты и долготы повреждены, если: значение равно NULL или "" или длина <2 </p>

Данные в столбце адреса повреждены, если значение равно NULL или "" или числу слов <2, а длина слова <2 </p>

Чтобы проверить это, я написал следующий скрипт на 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 ]

2 голосов
/ 29 марта 2012

Ваш первый запрос не имеет каких-либо существенных проблем с производительностью.Он будет работать с последовательным сканированием, оценивающим ваш предикат "поврежден".Проверка для == '' является избыточной с length(foo) < 2, поскольку длина ('') равна <2. У вас есть ошибка, когда вы указывали имена полей в вызовах length (), поэтому вы будете оценивать длину литералаимя поля вместо значения поля.Вы также не смогли проверить NULL, значение которого отличается от ''.Вы можете использовать функцию <code>coalesce для преобразования NULL в '' и захвата NULLS с проверкой длины.Вы также, кажется, не обращались к специальному правилу на основе слова для адреса.Это позже будет проблемой, если вы не расширите sqlite с помощью функции регулярного выражения.Я предлагаю аппроксимировать его как LIKE или GLOB.

Попробуйте эту альтернативу:

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 '%__ __%'

Вы обнаружите, что запрос дубликатов не работает, поскольку всегда есть хотя бы одна запись для сопоставления при самостоятельном присоединении кравенство.Вам необходимо исключить тестируемую запись на одной стороне объединения.Обычно это можно сделать, исключив первичный ключ.Вы не упомянули, есть ли у таблицы первичный ключ, но IIRC sqllite может предоставить вам прокси-сервер с ROWID.Примерно так:

SELECT L1.* 
FROM listings L1
where exists (
  select null
  from listings L2
  where L1.ROWID <> L2.ROWID
  AND L1.name = L2.name
  AND L1.telephone = L2.telephone
  AND L1.address = L2.address
  AND L1.latitude = L2.latitude
  AND L1.longitude = L2.longitude
)

Кстати, хотя вы подчеркивали эффективность в своем вопросе, важно, чтобы ваш код был корректным, прежде чем беспокоиться об эффективности.

0 голосов
/ 29 марта 2012

Я думаю, что вы делаете чрезмерную обработку. Поскольку длина строки '' равна 0, она соответствует условию length('') < 2. Таким образом, вам не нужно проверять, равняется ли поле '', поскольку оно уже отфильтровано условиями функции length.

Однако я не вижу, как вы проверяете нулевые значения. Я бы заменил все aField = '' на aField is null.

...