【问题标题】:How do I transform my SQL query to a MongoDB query?如何将我的 SQL 查询转换为 MongoDB 查询?
【发布时间】:2017-11-14 12:35:09
【问题描述】:

我将时间序列数据从 SQL 迁移到 MongoDB。我给你举个例子:

假设我们有一个带有 ID 的测量设备,每分钟读取一次值。因此,对于该设备,我们每天有 24 小时 * 60 分钟 = 1440 个值。

在 SQL 中,我们每天有 1440 行用于此设备:

ID          Timestamp                Value
400001       01.01.2017 00:00:00      ...
""           01.01.2017 00:01:00      ...
""                  ...               ...
""           01.01.2017 23:59:00      ...

我将数据迁移到 MongoDB,现在我每天有一个文档,值分布到 24 小时数组,分别包含包含值的 60 分钟字段(并且只有一个时间戳,日期为 XX-XX-XXXX 00: 00:00):

{ ID: 400001, Timestamp: 01.01.2017 00:00:00, Hours: [ 0: [0: ..., 1: ..., 2: ..., ....... 59: ... ], 1: [0: ..., 1: ..., 2: ..., ....... 59: ... ], . . 23: [0: ..., 1: ..., 2: ..., ....... 59: ... ] ] }

我的问题是: 我想把下面的SQL语句转换成mongoDB:

SELECT (Val) AS Val, (UNIX_TIMESTAMP(DATE_FORMAT(ArrivalTime, '%Y-%m-%d %H:%i:00'))) * 1000 AS timestmp FROM database WHERE ID = 400001 AND ArrivalTime BETWEEN FROM_UNIXTIME(1470002400) AND FROM_UNIXTIME(1475272800) ORDER BY ArrivalTime ASC

输出

由于在 MongoDB 中我只保存一天的时间戳,然后将值拆分为数组,因此我没有像 SQL 中那样为每个值设置时间戳。因此,例如,如果我想获取 01.01.2017 02:14:00 和 01.01.2017 18:38:00 之间的值,我该怎么做?

我做了一个 MongoDB 查询,它可以给我两天之间的值:

db.getCollection('test').aggregate([{$match: {ID: '400001', $and: [ {Timestamp_day: {$gte: new ISODate("2016-08-01 00:00:00.000Z")}}, {Timestamp_day: {$lte: new ISODate("2016-10-01 00:00:00.000Z")}}]}},{$unwind:"$Hours"}, {$unwind:"$Hours"}, {$group: {_id: '$Timestamp_day', Value: {$push: "$Hours"}}}, {$sort: {_id: 1}}]);

输出

但我需要它,就像在 SQL 中一样,我也可以只给出几个小时的值,并为每个值提供正确的时间戳。

我希望你能帮助我。

【问题讨论】:

  • 鉴于您的新数据结构,给出一整天的查询不再与给出一天中设定的几个小时的查询相同。这就像您突然决定在 SQL 的不同列中存储日期、小时和分钟一样——您将无法再使用初始查询。您必须在这里使用不同的查询(或多个查询)。
  • 你能发布一些示例数据吗?从跳过你的问题,我觉得你可以通过使用 $slice 来解决这个问题:docs.mongodb.com/manual/reference/operator/aggregation/slice
  • 我不知道 $slice 应该如何帮助我.. 有一篇文章说,你可以使用 mapReduce 来解决我的情况。可悲的是,没有给出例子:

标签: mysql sql mongodb transform


【解决方案1】:

这应该可以帮助您:

db.collection.aggregate([{
    $match: {
        "ID": '400001',
        "Timestamp_day": {
            $gte: new ISODate("2017-01-01T00:00:00.000Z"),
            $lte: new ISODate("2017-01-01T00:00:00.000Z")
        }
    }
}, {
    $unwind: {
      path: "$Hours",
      includeArrayIndex: "Hour"
    }
}, {
    $unwind: {
      path: "$Hours",
      includeArrayIndex: "Minute"
    }
}, {
    $project: {
        "_id": 0, // remove the "_id" field
        "Val": "$Hours", // rename "Hours" to "Val"
        "Timestamp": { // "resolve" our timestamp...
            $add: // ...by adding
            [
                { $multiply: [ "$Hour", 60 * 60 * 1000 ] }, // ...the number of hours in milliseconds
                { $multiply: [ "$Minute", 60 * 1000 ] }, // ...plus the number of minutes in milliseconds
                "$Timestamp_day", // to the "Timestamp_day" value
            ]
        }
    }
}, {
    $sort: {
        "Timestamp": 1 // oh well, sort by timestamp ascending
    }
}]);

输入文档为

{
    "_id" : ObjectId("5a0e7d096216d24dd605cdec"),
    "ID" : "400001",
    "Timestamp_day" : ISODate("2017-01-01T00:00:00.000Z"),
    "Hours" : [ 
        [ 
            0.0, 
            0.1, 
            2.0
        ], 
        [ 
            1.0, 
            1.1, 
            2.1
        ], 
        [ 
            2.0, 
            2.1, 
            2.2
        ]
    ]
}

结果如下所示:

/* 1 */
{
    "Val" : 0.0,
    "Timestamp" : ISODate("2017-01-01T00:00:00.000Z")
}

/* 2 */
{
    "Val" : 0.1,
    "Timestamp" : ISODate("2017-01-01T00:01:00.000Z")
}

/* 3 */
{
    "Val" : 2.0,
    "Timestamp" : ISODate("2017-01-01T00:02:00.000Z")
}

/* 4 */
{
    "Val" : 1.0,
    "Timestamp" : ISODate("2017-01-01T01:00:00.000Z")
}

/* 5 */
{
    "Val" : 1.1,
    "Timestamp" : ISODate("2017-01-01T01:01:00.000Z")
}

/* 6 */
{
    "Val" : 2.1,
    "Timestamp" : ISODate("2017-01-01T01:02:00.000Z")
}

/* 7 */
{
    "Val" : 2.0,
    "Timestamp" : ISODate("2017-01-01T02:00:00.000Z")
}

/* 8 */
{
    "Val" : 2.1,
    "Timestamp" : ISODate("2017-01-01T02:01:00.000Z")
}

/* 9 */
{
    "Val" : 2.2,
    "Timestamp" : ISODate("2017-01-01T02:02:00.000Z")
}

更新:

根据您的评论,您需要计算任何值与其各自先前值之间的差异。这可以通过以下方式完成 - 虽然可能有更好的方法来实现相同的事情......第一部分几乎与上面的解决方案相同,除了它添加了 $match 阶段以根据您的规范删除空值。

db.collection.aggregate([{
    $match: {
        "ID": '400001',
        "Timestamp_day": {
            $gte: new ISODate("2017-01-01T00:00:00.000Z"),
            $lte: new ISODate("2017-01-01T00:00:00.000Z")
        }
    }
}, {
    $unwind: {
      path: "$Hours",
      includeArrayIndex: "Hour"
    }
}, {
    $unwind: {
      path: "$Hours",
      includeArrayIndex: "Minute"
    }
}, {
    $match: {
        "Hours": { $ne: null } // get rid of all null values
    }
}, {
    $project: {
        "_id": 0, // remove the "_id" field
        "Val": "$Hours", // rename "Hours" to "Val"
        "Timestamp": { // "resolve" our timestamp...
            $add: // ...by adding
            [
                { $multiply: [ "$Hour", 60 * 60 * 1000 ] }, // ...the number of hours in milliseconds
                { $multiply: [ "$Minute", 60 * 1000 ] }, // ...plus the number of minutes in milliseconds
                "$Timestamp_day", // to the "Timestamp_day" value
            ]
        }
    }
}, {
    $sort: {
        "Timestamp": 1 // oh well, sort by timestamp ascending
    }
}, {
    $group: {
        "_id": null, // throw all documents in the same aggregated document
        "Docs": {
            $push: "$$ROOT" // and store our documents in an array
        }
    }
}, {
    $unwind: {
        path: "$Docs", // we flatten the "values" array
        includeArrayIndex: "Docs.Index", // this will give us the index of every element - there might be more elegant solutions using $map and $let...
    }
}, {
    $group: { // YES, unfortunately a second time... but this time we have the array index for each element
        "_id": null, // throw all documents in the same aggregated document
        "Docs": {
            $push: "$Docs" // and store our documents in an array
        }
    }
}, {
    $addFields: {
        "Docs": {
            $let: {
                vars: { "shiftedArray": { $concatArrays: [ [ null ], "$Docs.Val" ] } }, // shift value array by one to the right and put a null object at the start
                in: {
                    $map: {
                        input: "$Docs",
                        as: "d",
                        in: {
                            "Timestamp" : "$$d.Timestamp",
                            "Val": { $ifNull: [ { $abs: { $subtract: [ "$$d.Val", { $arrayElemAt: [ "$$shiftedArray", "$$d.Index" ] } ] } }, 0 ] }
                        }
                    }
                }
            }
        }
    }
}, {
    $unwind: "$Docs"
}, {
    $replaceRoot: {
        newRoot: "$Docs"
    }
}]);

使用您的示例数据集的结果如下所示:

/* 1 */
{
    "Timestamp" : ISODate("2017-01-01T00:00:00.000Z"),
    "Val" : 0.0
}

/* 2 */
{
    "Timestamp" : ISODate("2017-01-01T00:01:00.000Z"),
    "Val" : 0.0
}

/* 3 */
{
    "Timestamp" : ISODate("2017-01-01T00:02:00.000Z"),
    "Val" : 2.0
}

/* 4 */
{
    "Timestamp" : ISODate("2017-01-01T00:04:00.000Z"),
    "Val" : 3.0
}

/* 5 */
{
    "Timestamp" : ISODate("2017-01-01T00:05:00.000Z"),
    "Val" : 0.0
}

/* 6 */
{
    "Timestamp" : ISODate("2017-01-01T00:06:00.000Z"),
    "Val" : 1.0
}

【讨论】:

  • 你是我的英雄。谢谢!
  • Jederzeit gerne zu Diensten,@wuestenloewe! ;)
  • Wenn du wüsstest wie lange ich daran schon rumprobiere。 Hatte vorläufig eine Lösung mit mapreduce, aber die Performance war grauenhaft。 Hast mir Richtig Weitergeholfen :)
  • Könntest du mal einen Blick auf die "Antwort" werfen die ich weiter unten im Post verfasst habe? War leider etwas zu lang für einen Kommentar und ich bräuchte nochmal deine Hilfe。 Wäre echt super
【解决方案2】:

Eventuell könntest du mir nochmal helfen, auch ein Tipp würde reichen @dnickless.. Ich bräuchte eine Query die mir den Betrag der Differenz zum vorherig gemessenen Wert gibt (in einem bestimmten Zeitraum, zu einer bestimmten ID)。

又名贝斯皮尔: Timestamp_day: ISODate("2017-01-01T01:00:00.000Z"), Hours: [ [ 1.0, 1.0, -1.0, null, 2.0, 2.0, 3.0, ... ], [ ... ], ... ]

还有其他输出:

{
  'Timestamp' : ISODate("2017-01-01T00:00:00.000Z"),
  'Val' : 0.0 /* nix - 1.0 */
}

{
  'Timestamp' : ISODate("2017-01-01T00:01:00.000Z"),
  'Val' : 0.0 /* 1.0 - 1.0 */
}

{
  'Timestamp' : ISODate("2017-01-01T00:02:00.000Z"),
  'Val' : 2.0 /* 1.0 -  -1.0 */
}

{
  'Timestamp' : ISODate("2017-01-01T00:04:00.000Z"),
  'Val' : 3.0 /* -1.0 - (null) - 2.0 */
}

{
  'Timestamp' : ISODate("2017-01-01T00:05:00.000Z"),
  'Val' : 0.0 /* 2.0 - 2.0 */
}

{
  'Timestamp' : ISODate("2017-01-01T00:06:00.000Z"),
  'Val' : 1.0 /* 2.0 - 3.0 */
}

Hoffe es ist einigermaßen verständlich was ich meine

【讨论】:

  • 有没有更新gesehen? Funktioniert es 是这样吗?
  • Ich bekomme leider einen error, da man den $substract operator nicht auf strings anwenden kann
  • soweit ich weiß kann ich den typ nicht verändern oder? müsste also wenn dann direkt meine values als double speichern
  • ich ändere mal meine db dass die values als double gespeichert werden (macht eh um einiges mehr sinn) und probiere das skript dann aus. gebe dir dann ein update :)
  • funktioniert 超级。 vielen dank für deine hilfe!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多