2

我一直在开发一个 iPhone 应用程序,它查询一个中继我存储在 Amazon SimpleDB 中的数据的服务器。我有一个不同用户的“提交”数据库表。我正在与 Facebook 交互以检索 Facebook 朋友,并希望对“提交”进行查询以查找朋友的帖子 - 例如:

SELECT * FROM submissions WHERE userID = '00123' OR userID = '00124' OR .... 

(通过完整的朋友列表)

我认为这种选择语句会遇到亚马逊查询限制 -

[每个 Select 表达式的最大比较次数:20]

您能想出一种使用 SimpleDB 优雅地完成此任务的方法吗?我宁愿不必做一堆 20 人的查询。或者,我是否需要移动到不同的数据库包,然后进行跨表查询?

谢谢!

4

3 回答 3

4

有一种使用 SimpleDB 的方法,但它并不优雅,它更像是一种 hack,因为它需要您在提交项目中人为地复制 userid 属性。

这是基于这样一个事实,虽然每个 IN 谓词只能进行 20 次比较,但如果每个 IN 谓词命名不同的属性,则可以有 20 个 IN 谓词。因此,为表单的提交项添加额外的合成属性:

userID='00123' userID_2='00123' userID_3='00123' userID_4='00123' ... userID_20='00123'

对于给定的提交,它们都具有相同的值。然后,您可以通过单个查询获取多达 400 个朋友的提交:

SELECT * FROM submissions 
WHERE userID IN('00120','00121',...,'00139') OR
    `userID_2` IN('00140','00141',...,'00159') OR
    `userID_3` IN('00160','00161',...,'00179') OR
    `userID_4` IN('00180','00181',...,'00199') OR
    ...
    `userID_20` IN('00300','00301',...,'00319')

您可以在创建提交时填充 19 个额外属性(如果您有备用属性),听起来提交的用户不会改变。此外,您可能希望显式命名要返回的属性(而不是使用 * ),因为您现在在返回数据集中有 19 个您不关心的属性。

从数据模型的角度来看,这显然是一个 hack。但是话虽如此,对于拥有 400 名或更少朋友的用户来说,它正是您想要的:一个查询,因此您可以按日期或其他条件进行限制,按最近的排序,翻阅结果等。不幸的是,容量400 个将无法容纳所有 facebook 用户的朋友列表。因此,您可能仍然需要为大型朋友列表实现多查询解决方案。

我的建议是,如果 SimpleDB 满足您的应用程序的需求,但此问题除外,那么请考虑使用 hack。但是如果你需要反复做这样的事情,那么 SimpleDB 可能不是最好的选择。

于 2010-08-06T15:15:45.883 回答
2

您需要一个 IN 子句或一个临时表的连接。不幸的是,AmazonSimpleDB 有其局限性。正是因为这个原因,我们放弃了一个有前途的项目。在换档之前,我们走上了多线程的道路并使用了 NextToken 功能。

您可以对 SimpleDB 执行并行(多线程)查询以获取提交,每个查询最多查找 20 个用户 ID,然后将结果合并到一个列表中。不过,可能是时候考虑切换到 MySQL 或 SQL Server 以便能够将 ID 列表上传为临时表,然后进行简单的连接以获得结果。

于 2010-08-06T01:34:13.600 回答
1

我为 SimpleDB 创建了Simple Savant .NET 库,我碰巧有一些实用程序代码用于拆分和并行运行多个选择查询,同时将每个选择的 IN 子句限制为 20 个值。我可能会将此代码滚动到下一个 Savant 版本中,但这里适用于任何觉得它有用的人:

    /// <summary>
    /// Invokes select queries that use parameter lists (with IN clauses) by splitting the parameter list
    /// across multiple invocations that are invoked in parallel.
    /// </summary>
    /// <typeparam name="T">The item type</typeparam>
    /// <typeparam name="P">The select parameter type</typeparam>
    /// <param name="savant">The savant instance.</param>
    /// <param name="command">The command.</param>
    /// <param name="paramValues">The param values.</param>
    /// <param name="paramName">Name of the param.</param>
    /// <returns></returns>
    public static List<T> SelectWithList<T,P>(ISimpleSavantU savant, SelectCommand<T> command, List<P> paramValues, string paramName)
    {
        var allValues = SelectAttributesWithList(savant, command, paramValues, paramName);
        var typedValues = new List<T>();
        foreach (var values in allValues)
        {
            typedValues.Add((T)PropertyValues.CreateItem(typeof (T), values));
        }
        return typedValues;
    }

    /// <summary>
    /// Invokes select queries that use parameter lists (with IN clauses) by splitting the parameter list
    /// across multiple invocations that are invoked in parallel.
    /// </summary>
    /// <typeparam name="P">The select parameter type</typeparam>
    /// <param name="savant">The savant instance.</param>
    /// <param name="command">The command.</param>
    /// <param name="paramValues">The param values.</param>
    /// <param name="paramName">Name of the param.</param>
    /// <returns></returns>
    public static List<PropertyValues> SelectAttributesWithList<P>(ISimpleSavantU savant, SelectCommand command, List<P> paramValues, string paramName)
    {
        Arg.CheckNull("savant", savant);
        Arg.CheckNull("command", command);
        Arg.CheckNull("paramValues", paramValues);
        Arg.CheckNullOrEmpty("paramName", paramName);

        var allValues = new List<PropertyValues>();
        if (paramValues.Count == 0)
        {
            return allValues;
        }

        var results = new List<IAsyncResult>();
        do
        {
            var currentParams = paramValues.Skip(results.Count * MaxValueTestsPerSimpleDbQuery).Take(MaxValueTestsPerSimpleDbQuery).ToList();
            if (!currentParams.Any())
            {
                break;
            }
            var currentCommand = Clone(command);
            currentCommand.Reset();
            var parameter = currentCommand.GetParameter(paramName);
            parameter.Values.Clear();
            parameter.Values.AddRange(currentParams.Select(e => (object)e));
            var result = savant.BeginSelectAttributes(currentCommand, null, null);
            results.Add(result);
        } while (true);

        foreach (var result in results)
        {
            var values = ((ISimpleSavant2)savant).EndSelectAttributes(result);
            allValues.AddRange(values);
        }

        return allValues;
    }

    private static SelectCommand Clone(SelectCommand command)
    {
        var newParameters = new List<CommandParameter>();
        foreach (var parameter in command.Parameters)
        {
            var newParameter = new CommandParameter(parameter.Name, parameter.PropertyName, null);
            newParameter.Values.Clear();
            newParameters.Add(newParameter);
        }
        var newCommand = new SelectCommand(command.Mapping, command.CommandText, newParameters.ToArray())
            {
                MaxResultPages = command.MaxResultPages
            };
        return newCommand;
    }
于 2010-09-16T01:42:54.553 回答