sqlalchemy union 聯合查詢


在最近的工作中遇到一個問題,要將兩個字段相似的表里的數據統一起來展示在一個統計頁面中。如果是單純的展示數據那很簡單,兩個表查出來之后組合一下就完事了,但是有坑的地方就是分頁和按照時間搜索,這兩個功能決定了不可能單獨查詢兩張表。在同事的建議下,使用了union的聯合查詢,最終完成這個功能。做一個簡單的demo,記錄下這個功能。 

 

數據庫和sqlalchemy安裝請參考另一篇文章 sqlalchemy數據庫查詢小集合

 

定義數據表

 定義 兩張表,字段類型相同,但名稱不同。

#coding:utf-8

from sqlalchemy import Column,CHAR,INTEGER
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker

  
Base = declarative_base()

class User(Base):
    __tablename__ = "user"

    id = Column(CHAR(20),primary_key = True)
    name = Column(CHAR(20))
    age = Column(INTEGER)


class Teacher(Base):
    __tablename__ = "teacher"

    id = Column(CHAR(20),primary_key = True)
    tec_name = Column(CHAR(20))
    tec_age = Column(INTEGER)


engine = create_engine('mysql+mysqldb://root:12345678@localhost:3306/test')

def create_table(table_name):
    table_name.metadata.create_all(engine)
    print "創建成功"


def insert_data():

    DBSession = sessionmaker(bind=engine)
    session = DBSession()
    for x in range(10):
        temp = {}
        temp['id'] = x
        temp['name'] = 'user_' + str(x)
        temp['age'] = x
        user = User(**temp)
        session.add(user)

    for x in range(15):
        temp = {}
        temp['id'] = x
        temp['tec_name'] = 'tec_' + str(x)
        temp['tec_age'] = x * 2 
        tec = Teacher(**temp)
        session.add(tec)

    session.commit()
    session.close()
    print 'success'


if __name__ = '__main__':
    create_table(User)
    create_table(Teacher)
    insert_data()

 

 User表字段:

mysql> desc user;
+-------+----------+------+-----+---------+-------+
| Field | Type     | Null | Key | Default | Extra |
+-------+----------+------+-----+---------+-------+
| id    | char(20) | NO   | PRI | NULL    |       |
| name  | char(20) | YES  |     | NULL    |       |
| age   | int(11)  | YES  |     | NULL    |       |
+-------+----------+------+-----+---------+-------+
3 rows in set (0.00 sec)

 

User表數據: 

mysql> select * from user;
+----+--------+------+
| id | name   | age  |
+----+--------+------+
| 0  | user_0 |    0 |
| 1  | user_1 |    1 |
| 2  | user_2 |    2 |
| 3  | user_3 |    3 |
| 4  | user_4 |    4 |
| 5  | user_5 |    5 |
| 6  | user_6 |    6 |
| 7  | user_7 |    7 |
| 8  | user_8 |    8 |
| 9  | user_9 |    9 |
+----+--------+------+
10 rows in set (0.00 sec)

 

 Teacher表字段:

mysql> desc teacher;
+----------+----------+------+-----+---------+-------+
| Field    | Type     | Null | Key | Default | Extra |
+----------+----------+------+-----+---------+-------+
| id       | char(20) | NO   | PRI | NULL    |       |
| tec_name | char(20) | YES  |     | NULL    |       |
| tec_age  | int(11)  | YES  |     | NULL    |       |
+----------+----------+------+-----+---------+-------+
3 rows in set (0.00 sec)

mysql>  

 

teacher表數據:

mysql> select * from teacher;
+----+----------+---------+
| id | tec_name | tec_age |
+----+----------+---------+
| 0  | tec_0    |       0 |
| 1  | tec_1    |       2 |
| 10 | tec_10   |      20 |
| 11 | tec_11   |      22 |
| 12 | tec_12   |      24 |
| 13 | tec_13   |      26 |
| 14 | tec_14   |      28 |
| 2  | tec_2    |       4 |
| 3  | tec_3    |       6 |
| 4  | tec_4    |       8 |
| 5  | tec_5    |      10 |
| 6  | tec_6    |      12 |
| 7  | tec_7    |      14 |
| 8  | tec_8    |      16 |
| 9  | tec_9    |      18 |
+----+----------+---------+
15 rows in set (0.00 sec)

 

 查詢 

首先做一個簡單的查詢,將兩個表的數據分別查出來

def select():

    DBSession = sessionmaker(bind=engine)
    session = DBSession()
    table_data = session.query(User).all()
    session.close()

    for x in table_data:
        print x.name,'------>',x.age

    table_data = session.query(Teacher).all()
    session.close()
    for x in table_data:
        print x.tec_name,'------>',x.tec_age

 

查詢結果:

 Desktop  python union_one.py
/home/ljk/.local/lib/python2.7/site-packages/sqlalchemy/dialects/mysql/base.py:2514: Warning: '@@tx_isolation' is deprecated and will be removed in a future release. Please use '@@transaction_isolation' instead
  cursor.execute('SELECT @@tx_isolation')
user_0 ------> 0
user_1 ------> 1
user_2 ------> 2
user_3 ------> 3
user_4 ------> 4
user_5 ------> 5
user_6 ------> 6
user_7 ------> 7
user_8 ------> 8
user_9 ------> 9
-----------------------------------
tec_0 ------> 0
tec_1 ------> 2
tec_10 ------> 20
tec_11 ------> 22
tec_12 ------> 24
tec_13 ------> 26
tec_14 ------> 28
tec_2 ------> 4
tec_3 ------> 6
tec_4 ------> 8
tec_5 ------> 10
tec_6 ------> 12
tec_7 ------> 14
tec_8 ------> 16
tec_9 ------> 18

 

union查詢

union 查詢的關鍵字是 union ,首先將第一張表的數據全部查詢出來,然后將第二張表的數據全部查詢出來,最后將兩個數據使用union聯合成一張新表,這張新表可以再次被篩選過濾,分頁等。

def select():

    DBSession = sessionmaker(bind=engine)
    session = DBSession()
    table_data = session.query(User).all()
    session.close()

    # for x in table_data:
    #     print x.name,'------>',x.age

    # table_data = session.query(Teacher).all()
    # session.close()
    # for x in table_data:
    #     print x.tec_name,'------>',x.tec_age



    user_data = session.query(User.name,User.age)
    tec_data = session.query(Teacher.tec_name.label('name'), Teacher.tec_age.label('age'))


    result = user_data.union_all(tec_data)
    for x in result:
            print x.name,'------>',x.age

在上面的查詢中需要有一個注意點就是label,可以看到tec_data的查詢語句中使用了label這個屬性,該屬性的作用是將Teacher這張表查詢出來的tec_name 字段名稱變成name,已達到和User表字段的統一,只有兩張表的字段名稱一致,類型一致的情況下才能聯合查詢。

另外還使用了一個union_all 字段,該字段的意思是如果兩張表存在相同的記錄也要全部展示出來,想要讓相同的記錄合並起來使用union即可

查詢結果如下:

 

 Desktop  python union_one.py
/home/ljk/.local/lib/python2.7/site-packages/sqlalchemy/dialects/mysql/base.py:2514: Warning: '@@tx_isolation' is deprecated and will be removed in a future release. Please use '@@transaction_isolation' instead
  cursor.execute('SELECT @@tx_isolation')
user_0 ------> 0
user_1 ------> 1
user_2 ------> 2
user_3 ------> 3
user_4 ------> 4
user_5 ------> 5
user_6 ------> 6
user_7 ------> 7
user_8 ------> 8
user_9 ------> 9
tec_0 ------> 0
tec_1 ------> 2
tec_10 ------> 20
tec_11 ------> 22
tec_12 ------> 24
tec_13 ------> 26
tec_14 ------> 28
tec_2 ------> 4
tec_3 ------> 6
tec_4 ------> 8
tec_5 ------> 10
tec_6 ------> 12
tec_7 ------> 14
tec_8 ------> 16
tec_9 ------> 18

 

往往查詢出來還不是最終目的,還需要對查詢出來的數據過濾。查詢出來的數據不是一張正真的表,如果使用字段去匹配過濾條件呢?以查詢出age 大於 5為例 ,有兩種過濾方式:

1.使用User.age 作為篩選條件

2.使用Teacher.age 作為篩選條件 

規則就是使用兩張表里任意一張表的原始字段過濾即可,該過濾條件會在聯合查詢出來的結果起上作用。

 

使用 User 表字段 

def select():

    DBSession = sessionmaker(bind=engine)
    session = DBSession()
    table_data = session.query(User).all()
    session.close()

    # for x in table_data:
    #     print x.name,'------>',x.age

    # table_data = session.query(Teacher).all()
    # session.close()
    # for x in table_data:
    #     print x.tec_name,'------>',x.tec_age



    user_data = session.query(User.name,User.age)
    tec_data = session.query(Teacher.tec_name.label('name'), Teacher.tec_age.label('age'))


    # result = user_data.union_all(tec_data)
    # for x in result:
    #         print x.name,'------>',x.age


    result = user_data.union_all(tec_data).filter(User.age > 5)

    for x in result:
        print x.name,'------>',x.age

 

 Desktop  python union_one.py
/home/ljk/.local/lib/python2.7/site-packages/sqlalchemy/dialects/mysql/base.py:2514: Warning: '@@tx_isolation' is deprecated and will be removed in a future release. Please use '@@transaction_isolation' instead
  cursor.execute('SELECT @@tx_isolation')
user_6 ------> 6
user_7 ------> 7
user_8 ------> 8
user_9 ------> 9
tec_10 ------> 20
tec_11 ------> 22
tec_12 ------> 24
tec_13 ------> 26
tec_14 ------> 28
tec_3 ------> 6
tec_4 ------> 8
tec_5 ------> 10
tec_6 ------> 12
tec_7 ------> 14
tec_8 ------> 16
tec_9 ------> 18

 

 

使用 Teacher 表字段  

def select():

    DBSession = sessionmaker(bind=engine)
    session = DBSession()
    table_data = session.query(User).all()
    session.close()

    # for x in table_data:
    #     print x.name,'------>',x.age

    # table_data = session.query(Teacher).all()
    # session.close()
    # for x in table_data:
    #     print x.tec_name,'------>',x.tec_age

    user_data = session.query(User.name,User.age)
    tec_data = session.query(Teacher.tec_name.label('name'), Teacher.tec_age.label('age'))

    result = user_data.union_all(tec_data).filter(Teacher.tec_age>5)

    for x in result:
        print x.name,'------>',x.age
  

 

 Desktop  python union_one.py
/home/ljk/.local/lib/python2.7/site-packages/sqlalchemy/dialects/mysql/base.py:2514: Warning: '@@tx_isolation' is deprecated and will be removed in a future release. Please use '@@transaction_isolation' instead
  cursor.execute('SELECT @@tx_isolation')
user_6 ------> 6
user_7 ------> 7
user_8 ------> 8
user_9 ------> 9
tec_10 ------> 20
tec_11 ------> 22
tec_12 ------> 24
tec_13 ------> 26
tec_14 ------> 28
tec_3 ------> 6
tec_4 ------> 8
tec_5 ------> 10
tec_6 ------> 12
tec_7 ------> 14
tec_8 ------> 16
tec_9 ------> 18

 

關於union聯合查詢有一個說法很形象:join查詢就像是橫向擴展,將多張表的數據橫向組合在一起,而union像是縱向擴展,將多張表數據縱向排列起來


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM