【发布时间】:2019-09-02 17:20:35
【问题描述】:
我正在处理一个个人项目,我正在尝试编写一个复杂的查询:
获取属于某个用户的每一台设备
获取属于每个用户设备的每个传感器
获取每个用户设备传感器的最后记录值和时间戳。
我正在使用 Sqlite,并且我设法将查询编写为普通 SQL,但是,对于我的一生,我无法想出在 django 中执行此操作的方法。我查看了其他问题,尝试浏览文档,但无济于事。
我的模型:
class User(AbstractBaseUser):
email = models.EmailField()
class Device(models.Model):
user = models.ForeignKey(User)
name = models.CharField()
class Unit(models.Model):
name = models.CharField()
class SensorType(models.Model):
name = models.CharField()
unit = models.ForeignKey(Unit)
class Sensor(models.Model):
gpio_port = models.IntegerField()
device = models.ForeignKey(Device)
sensor_type = models.ForeignKey(SensorType)
class SensorData(models.Model):
sensor = models.ForeignKey(Sensor)
value = models.FloatField()
timestamp = models.DateTimeField()
这里是 SQL 查询:
SELECT acc.email,
dev.name as device_name,
stype.name as sensor_type,
sen.gpio_port as sensor_port,
sdata.value as sensor_latest_value,
unit.name as sensor_units,
sdata.latest as value_received_on
FROM devices_device as dev
INNER JOIN accounts_user as acc on dev.user_id = acc.id
INNER JOIN devices_sensor as sen on sen.device_id = dev.id
INNER JOIN devices_sensortype as stype on stype.id = sen.sensor_type_id
INNER JOIN devices_unit as unit on unit.id = stype.unit_id
LEFT JOIN (
SELECT MAX(sd.timestamp) latest, sd.value, sensor_id
FROM devices_sensordata as sd
INNER JOIN devices_sensor as s ON s.id = sd.sensor_id
GROUP BY sd.sensor_id) as sdata on sdata.sensor_id= sen.id
WHERE acc.id = 1
ORDER BY dev.id
我一直在使用 django shell 以找到一种使用 QuerySet API 实现此查询的方法,但我无法弄清楚...
我设法得到的最接近的是:
>>> sub = SensorData.objects.values('sensor_id', 'value').filter(sensor_id=OuterRef('pk')).order_by('-timestamp')[:1]
>>> Sensor.objects.annotate(data_id=Subquery(sub.values('sensor_id'))).filter(id=F('data_id')).values(...)
但是它有两个问题:
- 不包括在 SensorsData 中尚无任何值的传感器
- 如果我将 SensorData.values 字段包含到 .values() 中,我将开始获取之前记录的传感器值
如果有人能告诉我怎么做,或者至少告诉我我做错了什么,我将非常感激!
谢谢!
附:请原谅我的语法和拼写错误,我在半夜写这个,我很累。
编辑: 根据答案,我应该澄清: 我只想要每个传感器的最新传感器值。例如我在 sensordata 中有:
id | sensor_id | value | timestamp|
1 | 1 | 2 | <today> |
2 | 1 | 5 | <yesterday>|
3 | 2 | 3 | <yesterday>|
每个 sensor_id 只应返回最新的:
id | sensor_id | value | timestamp |
1 | 1 | 2 | <today> |
3 | 2 | 3 | <yesterday>|
或者,如果传感器在此表中还没有任何数据,我希望查询返回一条记录,其中值和时间戳为“null”(基本上是我的 SQL 查询中的左连接)。
EDIT2:
根据@ivissani 的回答,我设法制作了这个:
>>> latest_sensor_data = Sensor.objects.annotate(is_latest=~Exists(SensorData.objects.filter(sensor=OuterRef('id'),timestamp__gt=OuterRef('sensordata__timestamp')))).filter(is_latest=True)
>>> user_devices = latest_sensor_data.filter(device__user=1)
>>> for x in user_devices.values_list('device__name','sensor_type__name', 'gpio_port','sensordata__value', 'sensor_type__unit__name', 'sensordata__timestamp').order_by('device__name'):
... print(x)
这似乎可以完成这项工作。
这是它产生的 SQL:
SELECT
"devices_device"."name",
"devices_sensortype"."name",
"devices_sensor"."gpio_port",
"devices_sensordata"."value",
"devices_unit"."name",
"devices_sensordata"."timestamp"
FROM
"devices_sensor"
LEFT OUTER JOIN "devices_sensordata" ON (
"devices_sensor"."id" = "devices_sensordata"."sensor_id"
)
INNER JOIN "devices_device" ON (
"devices_sensor"."device_id" = "devices_device"."id"
)
INNER JOIN "devices_sensortype" ON (
"devices_sensor"."sensor_type_id" = "devices_sensortype"."id"
)
INNER JOIN "devices_unit" ON (
"devices_sensortype"."unit_id" = "devices_unit"."id"
)
WHERE
(
NOT EXISTS(
SELECT
U0."id",
U0."sensor_id",
U0."value",
U0."timestamp"
FROM
"devices_sensordata" U0
WHERE
(
U0."sensor_id" = ("devices_sensor"."id")
AND U0."timestamp" > ("devices_sensordata"."timestamp")
)
) = True
AND "devices_device"."user_id" = 1
)
ORDER BY
"devices_device"."name" ASC
【问题讨论】:
-
你想对数据做什么?您需要将它们放在平面表中(想想 Excel)还是作为嵌套对象(在树状结构中)?
-
两者都应该没问题。我将在视图中使用它们并将它们显示给用户。哪个更健壮。
标签: python django sqlite django-queryset