2017-10-13 47 views
2

我有在表中,看起來像這樣的數據:格式Django的查詢集,以輸出值作爲數組

src_id, dst_id, params 
int , int , array 

我查詢數據從數組 用下面的Django查詢

提取一些值

dataset = query_set.values_list('src_id', 'dst_id', *[e.field for e in settings])

我需要輸出的數據是這樣的:

[ 
[1,2,[a,b,c,d]], 
[3,4,[a,c,d,e]], 
... 
] 

values_list返回類似 (1,2,a,b,c,d)(3,4,a,c,d,e)...

元組的列表,所以我一直在做下面的元組值重新組合成一個數組:

[[d[0], d[1], d[2:]] for d in dataset] 

它工作正常,但我有100萬+行,而且速度很慢。它也使用了大量的內存。理想情況下,我想在SQL中正確格式化數據(通過Django),並將輸出直接推送到JSON串行器,而不必生成中間數組。

有沒有一種方法來優化此代碼?

我看着使用循環或lambda,但這並沒有太大的區別。我看着使用array,但它只需要原始類型,所以與陣列數組沒有太大的運氣。

我正在尋找一種方式來直接以正確的格式查詢數據庫和輸出的數據,如果可能的話在Django:

我看到Django的可以做切片陣列查找類似

dataset = query_set.values_list('src_id', 'dst_id', 'data__3_10')

例如

以獲得索引3和10之間的值,但我需要檢索特定索引(例如1,4,5,6,8,11),而不是切片。

有關如何格式化查詢輸出的任何提示? 是可能的,或只是太深奧的Django? SQL是否需要raw

由於

+0

格式化並不清楚數組中的字符串應該確實沒有引號,並且沒有字符串可以包含逗號或「]」。如果我知道,array(params)的個別元素可以在'.filter(data__3 = ...)'中用作查找,但不能在values_list中引用。你是如何實現的? – hynekcer

+0

問題首先不清楚。現在我從你的回答中瞭解到[1,2,[a,b,c,d]]並不是一個格式化的輸出,而是一個嵌套對象的符號表示,其中「a,b,c, d「是任何相同類型的值。一切都最終由JSON風格形成,這不是任何示例的一部分,因爲問題是一般的。 (現在修正了一個錯字) – hynekcer

回答

1

如果數據庫的PostgreSQL甲骨文然後內存要求可以基本上由施加到任何查詢集,包括values()values_list()查詢集iterator()方法在的Django 1.11降低。然後默認使用服務器端遊標,而不用在Django或數據庫驅動程序中進行任何緩存。閱讀文檔中的詳細信息。

演示

qs = query_set.values_list('src_id', 'dst_id', 'data') 
for row in qs.iterator(): 
    print(row) 

輸出

(3, 4, ['a', 'c', 'd', 'e']) 
... 

完整示例

qs = query_set.values_list('src_id', 'dst_id', 'data') 
with open(..., 'w') as f: 
    f.write('[\n') 
    for src_id, dst_id, data in qs.iterator(): 
     str_sub_array = ','.join(data[x] for x in column_settings) 
     f.write('[{},{},[{}]],\n'.format(src_id, dst_id, str_sub_array) 
    f.write(']\n') 

內存要求可以忽略不計。

+0

感謝您的信息。這可能有幫助,雖然我還沒有在1.11上。我可以看到迭代器如何減少內存請求,但問題更多的是將數據格式化爲查詢集之外的數組,而僅從原始數組中挑選必要的值。我還沒有找到一種方法在Django中做到這一點,只能通過編寫一個原始的SQL查詢。一旦數據格式正確,我所需要的就是返回它,但沒有它,我確實需要遍歷每一行,這很慢。 – MrE

+1

@MrE:更新了答案。如果需求是明確的,格式化是微不足道的,Django 1.11支持沒有命名遊標的內存需求是一個難題,原始的sql可能不會有太大的幫助。我認爲記憶和快速循環是必要的基礎。 – hynekcer

+0

我的目標是通過讓SQL返回正確的格式來完全消除格式化循環。我當前的循環需要2分鐘,而查詢需要10秒。迭代器可能爲循環節省內存,但最終我需要返回整個數據集,所以如果SQL一次性返回,我將節省構建輸出所需的內存。 這似乎沒有Django的方式,所以我會用原始SQL去。 – MrE

1

我能夠格式化輸出直接在Django不訴諸原始SQL,使用extra()特徵:

dataset = queryset.objects\ 
       .extra(select={'data': 'ARRAY[{}]'.format(",".join([fields])})\ 
       .values_list('src_id','dst_id','data') 
return dataset.iterator() 

這允許選擇SQL陣列中的特定索引因爲我需要。

返回迭代器(由@hynekcer建議),而不是結果數組本身,實際上對內存更好。

避免了重新格式化數據的循環,迭代器可以直接發送到接受迭代器作爲輸入的JSON序列化器。

+0

正常的迭代器不能直接被'json'或'simplejson'串行化。有些類似的解決方案可以通過'default'或'cls'參數或者通過子類化'JSONEncoder'來將迭代器轉換爲內部列表。對於大數據更好的解決方案是那些用迭代器類型交叉(子類化)類型列表以便將低內存需求和直接可串行性結合起來的解決方案。一種解決方案是我的[SerializableGenerator(迭代器)](https://stackoverflow.com/a/46841935/448474)。如果您通過'json.dump'將序列化到一個流而不是一個字符串,那麼輸出內存需求也很低。 – hynekcer

+0

可以編寫一個簡單的「查詢表達式函數」,它不需要「.extra()」,如果查詢使用更多的表,並且語法「relation_field__other_field」可以由後端編譯而沒有硬編碼表名,則可以使用該函數。對於一個簡單的情況,您的解決方案可能是最簡單的。 (不好意思,我的回答並不完整,你的問題最初是廣泛的,我在第一步之後等待了一個反饋。) – hynekcer

+0

'ujson'可以將迭代器作爲輸入並且工作正常。這就是我使用的。它也是那裏更快的串行器之一。 – MrE