SQL सारणीबद्ध डेटा के साथ काम करता है और लौटाता है (या संबंध, यदि आप इसे इस तरह से सोचना पसंद करते हैं, लेकिन सभी SQL तालिकाएँ संबंध नहीं हैं)। इसका तात्पर्य यह है कि एक नेस्टेड तालिका जैसा कि प्रश्न में दर्शाया गया है, वह सामान्य विशेषता नहीं है। Postgresql में कुछ इस तरह का उत्पादन करने के तरीके हैं, उदाहरण के लिए JSON या कंपोजिट के सरणियों का उपयोग करना, लेकिन यह केवल सारणीबद्ध डेटा लाने और एप्लिकेशन में नेस्टिंग करने के लिए पूरी तरह से संभव है। पायथन में itertools.groupby()
है।
, जो सॉर्ट किए गए डेटा को देखते हुए बिल को अच्छी तरह से फिट करता है।
त्रुटि column "incoming.id" must appear in the GROUP BY clause...
यह कह रहा है कि चयन सूची में गैर-समुच्चय, क्लॉज, आदि होने पर GROUP BY
में दिखाई देना चाहिए खंड या समग्र रूप से उपयोग किया जा सकता है, ऐसा न हो कि उनके पास संभवतः अनिश्चित मान . हों . दूसरे शब्दों में मान को समूह में केवल कुछ पंक्ति से चुनना होगा, क्योंकि GROUP BY
समूहीकृत पंक्तियों को एक पंक्ति में संघनित करता है , और यह किसी का भी अनुमान होगा कि उन्हें किस पंक्ति से चुना गया था। कार्यान्वयन इसकी अनुमति दे सकता है, जैसे SQLite करता है और MySQL करता था, लेकिन SQL मानक इस तरह की मनाही करता है। नियम का अपवाद तब होता है जब कोई कार्यात्मक निर्भरता
हो; GROUP BY
खंड गैर-समुच्चय निर्धारित करता है। तालिकाओं के बीच जुड़ने के बारे में सोचें A और बी ए . द्वारा समूहीकृत की प्राथमिक कुंजी। कोई फर्क नहीं पड़ता कि समूह में कौन सी पंक्ति सिस्टम A . के लिए मान चुनेगी के कॉलम से, वे वही होंगे क्योंकि समूहीकरण प्राथमिक कुंजी के आधार पर किया गया था।
3 बिंदु सामान्य इच्छित दृष्टिकोण को संबोधित करने के लिए, एक तरीका यह होगा कि आने वाले और बाहर जाने वाले संघ का चयन किया जाए, जो उनके टाइमस्टैम्प द्वारा आदेशित किया गया हो। चूंकि कोई विरासत पदानुक्रम नहीं है सेटअप--क्योंकि एक भी नहीं हो सकता है, मैं लेखांकन से परिचित नहीं हूं--कोर और सादे परिणाम टुपल्स का उपयोग करने से इस मामले में चीजें आसान हो जाती हैं:
incoming = select([literal('incoming').label('type'), Incoming.__table__]).\
where(Incoming.accountID == accountID)
outgoing = select([literal('outgoing').label('type'), Outgoing.__table__]).\
where(Outgoing.accountID == accountID)
all_entries = incoming.union(outgoing)
all_entries = all_entries.order_by(all_entries.c.timestamp)
all_entries = db_session.execute(all_entries)
फिर नेस्टेड संरचना बनाने के लिए itertools.groupby()
प्रयोग किया जाता है:
date_groups = groupby(all_entries, lambda ent: ent.timestamp.date())
date_groups = [(k, [dict(ent) for ent in g]) for k, g in date_groups]
अंतिम परिणाम दिनांक के 2-टुपल्स की सूची और आरोही क्रम में प्रविष्टियों के शब्दकोशों की सूची है। काफी ओआरएम समाधान नहीं है, लेकिन काम पूरा हो जाता है। एक उदाहरण:
In [55]: session.add_all([Incoming(accountID=1, amount=1, description='incoming',
...: timestamp=datetime.utcnow() - timedelta(days=i))
...: for i in range(3)])
...:
In [56]: session.add_all([Outgoing(accountID=1, amount=2, description='outgoing',
...: timestamp=datetime.utcnow() - timedelta(days=i))
...: for i in range(3)])
...:
In [57]: session.commit()
In [58]: incoming = select([literal('incoming').label('type'), Incoming.__table__]).\
...: where(Incoming.accountID == 1)
...:
...: outgoing = select([literal('outgoing').label('type'), Outgoing.__table__]).\
...: where(Outgoing.accountID == 1)
...:
...: all_entries = incoming.union(outgoing)
...: all_entries = all_entries.order_by(all_entries.c.timestamp)
...: all_entries = db_session.execute(all_entries)
In [59]: date_groups = groupby(all_entries, lambda ent: ent.timestamp.date())
...: [(k, [dict(ent) for ent in g]) for k, g in date_groups]
Out[59]:
[(datetime.date(2019, 9, 1),
[{'accountID': 1,
'amount': 1.0,
'description': 'incoming',
'id': 5,
'timestamp': datetime.datetime(2019, 9, 1, 20, 33, 6, 101521),
'type': 'incoming'},
{'accountID': 1,
'amount': 2.0,
'description': 'outgoing',
'id': 4,
'timestamp': datetime.datetime(2019, 9, 1, 20, 33, 29, 420446),
'type': 'outgoing'}]),
(datetime.date(2019, 9, 2),
[{'accountID': 1,
'amount': 1.0,
'description': 'incoming',
'id': 4,
'timestamp': datetime.datetime(2019, 9, 2, 20, 33, 6, 101495),
'type': 'incoming'},
{'accountID': 1,
'amount': 2.0,
'description': 'outgoing',
'id': 3,
'timestamp': datetime.datetime(2019, 9, 2, 20, 33, 29, 420419),
'type': 'outgoing'}]),
(datetime.date(2019, 9, 3),
[{'accountID': 1,
'amount': 1.0,
'description': 'incoming',
'id': 3,
'timestamp': datetime.datetime(2019, 9, 3, 20, 33, 6, 101428),
'type': 'incoming'},
{'accountID': 1,
'amount': 2.0,
'description': 'outgoing',
'id': 2,
'timestamp': datetime.datetime(2019, 9, 3, 20, 33, 29, 420352),
'type': 'outgoing'}])]
जैसा कि उल्लेख किया गया है, Postgresql JSON की एक सरणी का उपयोग करने के समान ही बहुत अधिक परिणाम उत्पन्न कर सकता है:
from sqlalchemy.dialects.postgresql import aggregate_order_by
incoming = select([literal('incoming').label('type'), Incoming.__table__]).\
where(Incoming.accountID == accountID)
outgoing = select([literal('outgoing').label('type'), Outgoing.__table__]).\
where(Outgoing.accountID == accountID)
all_entries = incoming.union(outgoing).alias('all_entries')
day = func.date_trunc('day', all_entries.c.timestamp)
stmt = select([day,
func.array_agg(aggregate_order_by(
func.row_to_json(literal_column('all_entries.*')),
all_entries.c.timestamp))]).\
group_by(day).\
order_by(day)
db_session.execute(stmt).fetchall()
अगर वास्तव में Incoming
और Outgoing
एक सामान्य आधार के बच्चों के रूप में सोचा जा सकता है, उदाहरण के लिए Entry
, यूनियनों का उपयोग कुछ हद तक कंक्रीट टेबल इनहेरिटेंस के साथ स्वचालित किया जा सकता है
:
from sqlalchemy.ext.declarative import AbstractConcreteBase
class Entry(AbstractConcreteBase, Base):
pass
class Incoming(Entry):
__tablename__ = 'incoming'
id = Column(Integer, primary_key=True)
accountID = Column(Integer, ForeignKey('account.id'))
amount = Column(Float, nullable=False)
description = Column(Text, nullable=False)
timestamp = Column(TIMESTAMP, nullable=False)
account = relationship("Account", back_populates="incomings")
__mapper_args__ = {
'polymorphic_identity': 'incoming',
'concrete': True
}
class Outgoing(Entry):
__tablename__ = 'outgoing'
id = Column(Integer, primary_key=True)
accountID = Column(Integer, ForeignKey('account.id'))
amount = Column(Float, nullable=False)
description = Column(Text, nullable=False)
timestamp = Column(TIMESTAMP, nullable=False)
account = relationship("Account", back_populates="outgoings")
__mapper_args__ = {
'polymorphic_identity': 'outgoing',
'concrete': True
}
दुर्भाग्य से AbstractConcreteBase
configure_mappers()
पर मैन्युअल कॉल की आवश्यकता है
जब सभी आवश्यक वर्गों को परिभाषित किया गया हो; इस मामले में User
. को परिभाषित करने के बाद जल्द से जल्द संभावना है , क्योंकि Account
रिश्तों के माध्यम से इस पर निर्भर करता है:
from sqlalchemy.orm import configure_mappers
configure_mappers()
फिर सभी Incoming
. लाने के लिए और Outgoing
एकल बहुरूपी ORM क्वेरी में Entry
. का उपयोग करें :
session.query(Entry).\
filter(Entry.accountID == accountID).\
order_by(Entry.timestamp).\
all()
और itertools.groupby()
use का उपयोग करने के लिए आगे बढ़ें Incoming
. की परिणामी सूची में ऊपर के रूप में और Outgoing
।