реализация алгоритма в SQL-запросе - PullRequest
1 голос
/ 14 декабря 2011

У меня есть база данных, которая имеет 2 таблицы:

CREATE TABLE RecipeDB (
    RecipeID INT PRIMARY KEY AUTO_INCREMENT,
    Name VARCHAR,
    Recipe VARCHAR,
    Origin VARCHAR,
    Category VARCHAR,
    Favoured BOOL);

CREATE TABLE IngredientDB (
    RecipeID REFERENCES RecipeDB.RecipeID,
    Ingredient VARCHAR,
    Quantity VARCHAR);

(Отношение один-ко-многим между рецептом и ингредиентами)

У меня также есть actionScript, в котором у меня есть ingArr: Массив строк ингредиентов.

Теперь я хотел бы реализовать следующие запросы:

1) Выберите (все поля) один рецепт, который содержит большинство ингредиентов из массива. Если у нескольких записей одинаковое количество совпадений, разделите количество совпадений на общее количество ингредиентов в рецепте и верните тот, который имеет наибольшее соотношение. Если совпадений нет, ничего не вернуть.

2) Как указано выше, но вернуть 10 рецептов с наибольшим количеством совпадений и не выполнять проверку на равное количество совпадений. Сортировать результаты по количеству совпадений.

Есть идеи, как составлять эти запросы в SQLite?

Ответы [ 2 ]

1 голос
/ 15 декабря 2011

(оператор SQL, указанный ниже, предназначен для SQLite)

  • Таким образом, для второго вам потребуется 10 лучших рецептов, соответствующих большинству ингредиентов

Вам нужно:

  • подсчитать строку, соответствующую вашему списку ингредиентов (используйте оператор IN)
  • упорядочить результат по наилучшему количеству в порядке убывания (4,3,2, ...)
  • ограничить результат 10

Таким образом, выражение sql выглядит как

SELECT
 r.RecipeId, COUNT(1) cnt, r.Name, r.Recipe, r.Origin, r.Category, r.Favoured
FROM 
 RecipeDB r
 INNER JOIN IngredientDB i USING(RecipeID)
WHERE 
 i.Ingredient in ('ingr_1',..,'ingr_x')
GROUP BY 1
ORDER BY 2 DESC
LIMIT 10

с использованием AIR + AS3 itможет быть что-то вроде этого:

var sqls:SQLStatement = new SQLStatement()
sqls.sqlConnection = YOUR SQL CONNECTION

// your ingredient list
var ingredients:Array = ['i2', 'i3', 'i4']

// use to build the in parameter array 
var inParams:Array = []

// fill parameter values
for(var i:int = 0; i < ingredients.length; ++i) {
 inParams[i] = '?'
 sqls.parameters[i] = ingredients[i]
}

// build the query
var qry:String = "SELECT r.RecipeId, COUNT(1) cnt, r.Name, r.Recipe, r.Origin,"+
"r.Category, r.Favoured FROM RecipeDB r INNER JOIN IngredientDB i USING(RecipeID)"+
"WHERE i.Ingredient in (" + inParams.join(',') + ") GROUP BY 1 "+
"ORDER BY 2 DESC LIMIT 10"

// set the query
sqls.text = qry

//execute
sqls.execute()

  • И для первой же идеи, как указано выше, но вам нужно также подсчитать все ингредиенты, присутствующие в рецепте, чтобы обеспечить соотношение между соответствием /всего

Вам нужно:

  • подсчитать строку, соответствующую вашему списку ингредиентов (используйте оператор IN)
  • сделать ранг делением предыдущего подсчетапо всему суммарному ингредиенту
  • получите наилучшее совпадение
  • ограничьте результат 1

Итак, статистика sqlЭлемент выглядит так:

SELECT 
 i1.RecipeId, (cast(rs.cnt as real) / cast (COUNT(1) as real)) rank,
 rs.Name, rs.Recipe, rs.Origin, rs.Category, rs.Favoured
FROM 
 IngredientDB i1 
 INNER JOIN (
  SELECT
   r.RecipeId, COUNT(1) cnt, r.Name, r.Recipe, r.Origin, r.Category, r.Favoured
  FROM
   RecipeDB r
   INNER JOIN IngredientDB i USING(RecipeID)
  WHERE
   i.Ingredient in ('ingr_1',..,'ingr_x')
  GROUP BY 1
 ) rs USING (RecipeId)
GROUP BY 1
ORDER BY 2 DESC
LIMIT 1

Используя ту же логику, что и в первом примере, ваш запрос может быть записан как:

var ingredients:Array = ['i2', 'i3', 'i4']
var inParams:Array = []
for(var i:int = 0; i < ingredients.length; ++i) {
 inParams[i] = '?'
 sqls.parameters[i] = ingredients[i]
}

var qry:String = "SELECT i1.RecipeId, (cast(rs.cnt as real) / cast (COUNT(1) as real)) rank,"+
"rs.Name, rs.Recipe, rs.Origin, rs.Category, rs.Favoured "+
"FROM IngredientDB i1 INNER JOIN ("+
"SELECT r.RecipeId, COUNT(1) cnt, r.Name, r.Recipe, r.Origin, r.Category, r.Favoured "+
"FROM RecipeDB r INNER JOIN IngredientDB i USING(RecipeID) "+
"WHERE i.Ingredient in (" + inParams.join(',') + ") GROUP BY 1) rs USING (RecipeId) "+
"GROUP BY 1 ORDER BY 2 DESC LIMIT 1"
0 голосов
/ 15 декабря 2011

Запросы можно немного настроить, но приведенный ниже T-SQL демонстрирует ответы, которые вы ищете, достаточно читабельным способом.

BEGIN
-- setup test
DECLARE @one TABLE(id INT, name VARCHAR(10))
DECLARE @many TABLE(pid INT, name VARCHAR(10))
INSERT INTO @one VALUES 
    (1, 'AAA'),
    (2, 'BBB'),
    (3, 'CCC')
INSERT INTO @many VALUES    
    (1, 'x'),(1, 'y'),(1, 'z'),
    (2, 'x'),(2, 'y'),
    (3, 'z')
--
-- WHERE m.name IN ('x', 'y')
--  'x', 'y' represent your list of ingrediants
-- answer 1
SELECT * FROM @one WHERE id = (
    SELECT TOP 1 x.id FROM (
        SELECT o.id, COUNT(o.id) 'match', (SELECT COUNT(*) FROM @many WHERE pid=o.id) 'total' FROM @one o 
            INNER JOIN @many m ON o.id = m.pid
            WHERE m.name IN ('x', 'y', 'z')
            GROUP BY o.id
    ) as x ORDER BY x.match DESC, x.match/x.total DESC
)
-- answer 2
SELECT * FROM @one WHERE id IN (
    SELECT TOP 10 x.id FROM (
        SELECT o.id, COUNT(o.id) 'match' FROM @one o 
            INNER JOIN @many m ON o.id = m.pid
            WHERE m.name IN ('x', 'y')
            GROUP BY o.id
    ) as x ORDER BY x.match DESC
)
END
...