需求:
公司的数据分析师,提交一个sql, 一般都三四百行。由于数据安全的需要,不能开放所有的数据库和数据表给数据分析师查询,所以需要解析sql中的数据库和表,与权限管理系统中记录的数据库和表权限信息比对,实现非法查询的拦截。
解决办法:
在解决这个问题前,现在github找了一下轮子,发现python下面除了sql parse没什么好的解析数据库和表的轮轮。到是在java里面找到presto-parser解析的比较准。于是自己结合sql parse源码写了个类,供大家参考,测试了一下,检测还是准的。
测试sql
select b.product_name "产品", count(a.order_id) "订单量", b.selling_price_max "销售价", b.gross_profit_rate_max/100 "毛利率", case when b.business_type =1 then '自营消化' when b.business_type =2 then '服务商消化' end "消化模式" from(select 'CRM签单' label,date(d.update_ymd) close_ymd,c.product_name,c.product_id, a.order_id,cast(a.recipient_amount as double) amt,d.cost from mysql4.dataview_fenxiao.fx_order a left join mysql4.dataview_fenxiao.fx_order_task b on a.order_id = b.order_id left join mysql7.dataview_trade.ddc_product_info c on cast(c.product_id as varchar) = a.product_ids and c.snapshot_version = 'SELLING' inner join (select t1.par_order_id,max(t1.update_ymd) update_ymd, sum(case when t4.product2_type = 1 and t5.shop_id is not null then t5.price else t1.order_hosted_price end) cost from hive.bdc_dwd.dw_mk_order t1 left join hive.bdc_dwd.dw_mk_order_status t2 on t1.order_id = t2.order_id and t2.acct_day = substring(cast(DATE_ADD('day',-1,CURRENT_DATE) as varchar),9,2) left join mysql7.dataview_trade.mk_order_merchant t3 on t1.order_id = t3.order_id left join mysql7.dataview_trade.ddc_product_info t4 on t4.product_id = t3.MERCHANT_ID and t4.snapshot_version = 'SELLING' left join mysql4.dataview_scrm.sc_tprc_product_info t5 on t5.product_id = t4.product_id and t5.shop_id = t1.seller_id where t1.acct_day = substring(cast(DATE_ADD('day',-1,CURRENT_DATE) as varchar),9,2) and t2.valid_state in (100,200) ------有效订单 and t1.order_mode = 10 --------产品消耗订单 and t2.complete_state = 1 -----订单已经完成 group by t1.par_order_id ) d on d.par_order_id = b.task_order_id where c.product_type = 0 and date(from_unixtime(a.last_recipient_time)) > date('2016-01-01') and a.payee_type <> 1 -----------已收款 UNION ALL select '企业管家消耗' label,date(c.update_ymd) close_ymd,b.product_name,b.product_id, a.task_id,(case when a.yb_price = 0 and b.product2_type = 1 then b.selling_price_min else a.yb_price end) amt, (case when a.yb_price = 0 and b.product2_type = 2 then 0 when b.product2_type = 1 and e.shop_id is not null then e.price else c.order_hosted_price end) cost from mysql8.dataview_tprc.tprc_task a left join mysql7.dataview_trade.ddc_product_info b on a.product_id = b.product_id and b.snapshot_version = 'SELLING' inner join hive.bdc_dwd.dw_mk_order c on a.order_id = c.order_id and c.acct_day = substring(cast(DATE_ADD('day',-1,CURRENT_DATE) as varchar),9,2) left join hive.bdc_dwd.dw_mk_order_status d on d.order_id = c.order_id and d.acct_day = substring(cast(DATE_ADD('day',-1,CURRENT_DATE) as varchar),9,2) left join mysql4.dataview_scrm.sc_tprc_product_info e on e.product_id = b.product_id and e.shop_id = c.seller_id where d.valid_state in (100,200) and d.complete_state = 1 and c.order_mode = 10 union ALL select '交易管理系统' label,date(t6.close_ymd) close_ymd,t4.product_name,t4.product_id, t1.order_id,(t1.order_hosted_price-t1.order_refund_price) amt, (case when t1.order_mode <> 11 then t7.user_amount when t1.order_mode = 11 and t4.product2_type = 1 and t5.shop_id is not null then t5.price else t8.cost end) cost from hive.bdc_dwd.dw_mk_order t1 left join hive.bdc_dwd.dw_mk_order_business t2 on t1.order_id = t2.order_id and t2.acct_day=substring(cast(DATE_ADD('day',-1,CURRENT_DATE) as varchar),9,2) left join mysql7.dataview_trade.mk_order_merchant t3 on t1.order_id = t3.order_id left join mysql7.dataview_trade.ddc_product_info t4 on t4.product_id = t3.MERCHANT_ID and t4.snapshot_version = 'SELLING' left join mysql4.dataview_scrm.sc_tprc_product_info t5 on t5.product_id = t4.product_id and t5.shop_id = t1.seller_id left join hive.bdc_dwd.dw_fact_task_ss_daily t6 on t6.task_id = t2.task_id and t6.acct_time=date_format(date_add('day',-1,current_date),'%Y-%m-%d') left join (select a.task_id,sum(a.user_amount) user_amount from hive.bdc_dwd.dw_fn_deal_asyn_order a where a.is_new=1 and a.service='Trade_Payment' and a.state=1 and a.acct_day=substring(cast(DATE_ADD('day',-1,CURRENT_DATE) as varchar),9,2) group by a.task_id)t7 on t7.task_id = t2.task_id left join (select t1.par_order_id,sum(t1.order_hosted_price - t1.order_refund_price) cost from hive.bdc_dwd.dw_mk_order t1 where t1.acct_day = substring(cast(DATE_ADD('day',-1,CURRENT_DATE) as varchar),9,2) and t1.order_type = 1 and t1.order_stype = 4 and t1.order_mode = 12 group by t1.par_order_id) t8 on t1.order_id = t8.par_order_id where t1.acct_day = substring(cast(DATE_ADD('day',-1,CURRENT_DATE) as varchar),9,2) and t1.order_type = 1 and t1.order_stype in (4,5) and t1.order_mode <> 12 and t4.product_id is not null and t1.order_hosted_price > 0 and t6.is_deal = 1 and t6.close_ymd >= '2018-12-31' )a left join mysql7.dataview_trade.ddc_product_info b on a.product_id = b.product_id and b.snapshot_version = 'SELLING' where b.product2_type = 1 -------标品 and close_ymd between DATE_ADD('day',-7,CURRENT_DATE) and DATE_ADD('day',-1,CURRENT_DATE) GROUP BY b.product_name, b.selling_price_max, b.gross_profit_rate_max/100, b.actrul_supply_num, case when b.business_type =1 then '自营消化' when b.business_type =2 then '服务商消化' end order by count(a.order_id) desc limit 10
可以看到该sql比较杂,也没有格式化,不太好提取数据库和表。所以第一步需要对sql进行格式化
直接上代码:
# coding=utf-8 from __future__ import absolute_import from __future__ import division from __future__ import print_function from __future__ import unicode_literals import sqlparse from sqlparse.sql import Identifier, IdentifierList from sqlparse.tokens import Keyword, Name RESULT_OPERATIONS = {'UNION', 'INTERSECT', 'EXCEPT', 'SELECT'} ON_KEYWORD = 'ON' PRECEDES_TABLE_NAME = {'FROM', 'JOIN', 'DESC', 'DESCRIBE', 'WITH'} class BaseExtractor(object): def __init__(self, sql_statement): self.sql = sqlparse.format(sql_statement, reindent=True, keyword_case='upper') self._table_names = set() self._alias_names = set() self._limit = None self._parsed = sqlparse.parse(self.stripped()) for statement in self._parsed: self.__extract_from_token(statement) self._limit = self._extract_limit_from_query(statement) self._table_names = self._table_names - self._alias_names @property def tables(self): return self._table_names @property def limit(self): return self._limit def is_select(self): return self._parsed[0].get_type() == 'SELECT' def is_explain(self): return self.stripped().upper().startswith('EXPLAIN') def is_readonly(self): return self.is_select() or self.is_explain() def stripped(self): return self.sql.strip(' \t\n;') def get_statements(self): statements = [] for statement in self._parsed: if statement: sql = str(statement).strip(' \n;\t') if sql: statements.append(sql) return statements @staticmethod def __precedes_table_name(token_value): for keyword in PRECEDES_TABLE_NAME: if keyword in token_value: return True return False @staticmethod def get_full_name(identifier): if len(identifier.tokens) > 1 and identifier.tokens[1].value == '.': return '{}.{}'.format(identifier.tokens[0].value, identifier.tokens[2].value) return identifier.get_real_name() @staticmethod def __is_result_operation(keyword): for operation in RESULT_OPERATIONS: if operation in keyword.upper(): return True return False @staticmethod def __is_identifier(token): return isinstance(token, (IdentifierList, Identifier)) def __process_identifier(self, identifier): if '(' not in '{}'.format(identifier): self._table_names.add(self.get_full_name(identifier)) return # store aliases if hasattr(identifier, 'get_alias'): self._alias_names.add(identifier.get_alias()) if hasattr(identifier, 'tokens'): # some aliases are not parsed properly if identifier.tokens[0].ttype == Name: self._alias_names.add(identifier.tokens[0].value) self.__extract_from_token(identifier) def as_create_table(self, table_name, overwrite=False): exec_sql = '' sql = self.stripped() if overwrite: exec_sql = 'DROP TABLE IF EXISTS {};\n'.format(table_name) exec_sql += 'CREATE TABLE {} AS \n{}'.format(table_name, sql) return exec_sql def __extract_from_token(self, token): if not hasattr(token, 'tokens'): return table_name_preceding_token = False for item in token.tokens: if item.is_group and not self.__is_identifier(item): self.__extract_from_token(item) if item.ttype in Keyword: if self.__precedes_table_name(item.value.upper()): table_name_preceding_token = True continue if not table_name_preceding_token: continue if item.ttype in Keyword or item.value == ',': if (self.__is_result_operation(item.value) or item.value.upper() == ON_KEYWORD): table_name_preceding_token = False continue # FROM clause is over break if isinstance(item, Identifier): self.__process_identifier(item) if isinstance(item, IdentifierList): for token in item.tokens: if self.__is_identifier(token): self.__process_identifier(token) def _get_limit_from_token(self, token): if token.ttype == sqlparse.tokens.Literal.Number.Integer: return int(token.value) elif token.is_group: return int(token.get_token_at_offset(1).value) def _extract_limit_from_query(self, statement): limit_token = None for pos, item in enumerate(statement.tokens): if item.ttype in Keyword and item.value.lower() == 'limit': limit_token = statement.tokens[pos + 2] return self._get_limit_from_token(limit_token) def get_query_with_new_limit(self, new_limit): if not self._limit: return self.sql + ' LIMIT ' + str(new_limit) limit_pos = None tokens = self._parsed[0].tokens # Add all items to before_str until there is a limit for pos, item in enumerate(tokens): if item.ttype in Keyword and item.value.lower() == 'limit': limit_pos = pos break limit = tokens[limit_pos + 2] if limit.ttype == sqlparse.tokens.Literal.Number.Integer: tokens[limit_pos + 2].value = new_limit elif limit.is_group: tokens[limit_pos + 2].value = ( '{}, {}'.format(next(limit.get_identifiers()), new_limit) ) str_res = '' for i in tokens: str_res += str(i.value) return str_res class SqlExtractor(BaseExtractor): """提取sql语句""" @staticmethod def get_full_name(identifier, including_dbs=False): if len(identifier.tokens) > 1 and identifier.tokens[1].value == '.': a = identifier.tokens[0].value b = identifier.tokens[2].value db_table = (a, b) full_tree = '{}.{}'.format(a, b) if len(identifier.tokens) == 3: return full_tree else: i = identifier.tokens[3].value c = identifier.tokens[4].value if i == ' ': return full_tree full_tree = '{}.{}.{}'.format(a, b, c) return full_tree return None, None if __name__ == '__main__': sql = """select b.product_name "产品", count(a.order_id) "订单量", b.selling_price_max "销售价", b.gross_profit_rate_max/100 "毛利率", case when b.business_type =1 then '自营消化' when b.business_type =2 then '服务商消化' end "消化模式" from(select 'CRM签单' label,date(d.update_ymd) close_ymd,c.product_name,c.product_id, a.order_id,cast(a.recipient_amount as double) amt,d.cost from mysql4.dataview_fenxiao.fx_order a left join mysql4.dataview_fenxiao.fx_order_task b on a.order_id = b.order_id left join mysql7.dataview_trade.ddc_product_info c on cast(c.product_id as varchar) = a.product_ids and c.snapshot_version = 'SELLING' inner join (select t1.par_order_id,max(t1.update_ymd) update_ymd, sum(case when t4.product2_type = 1 and t5.shop_id is not null then t5.price else t1.order_hosted_price end) cost from hive.bdc_dwd.dw_mk_order t1 left join hive.bdc_dwd.dw_mk_order_status t2 on t1.order_id = t2.order_id and t2.acct_day = substring(cast(DATE_ADD('day',-1,CURRENT_DATE) as varchar),9,2) left join mysql7.dataview_trade.mk_order_merchant t3 on t1.order_id = t3.order_id left join mysql7.dataview_trade.ddc_product_info t4 on t4.product_id = t3.MERCHANT_ID and t4.snapshot_version = 'SELLING' left join mysql4.dataview_scrm.sc_tprc_product_info t5 on t5.product_id = t4.product_id and t5.shop_id = t1.seller_id where t1.acct_day = substring(cast(DATE_ADD('day',-1,CURRENT_DATE) as varchar),9,2) and t2.valid_state in (100,200) ------有效订单 and t1.order_mode = 10 --------产品消耗订单 and t2.complete_state = 1 -----订单已经完成 group by t1.par_order_id ) d on d.par_order_id = b.task_order_id where c.product_type = 0 and date(from_unixtime(a.last_recipient_time)) > date('2016-01-01') and a.payee_type <> 1 -----------已收款 UNION ALL select '企业管家消耗' label,date(c.update_ymd) close_ymd,b.product_name,b.product_id, a.task_id,(case when a.yb_price = 0 and b.product2_type = 1 then b.selling_price_min else a.yb_price end) amt, (case when a.yb_price = 0 and b.product2_type = 2 then 0 when b.product2_type = 1 and e.shop_id is not null then e.price else c.order_hosted_price end) cost from mysql8.dataview_tprc.tprc_task a left join mysql7.dataview_trade.ddc_product_info b on a.product_id = b.product_id and b.snapshot_version = 'SELLING' inner join hive.bdc_dwd.dw_mk_order c on a.order_id = c.order_id and c.acct_day = substring(cast(DATE_ADD('day',-1,CURRENT_DATE) as varchar),9,2) left join hive.bdc_dwd.dw_mk_order_status d on d.order_id = c.order_id and d.acct_day = substring(cast(DATE_ADD('day',-1,CURRENT_DATE) as varchar),9,2) left join mysql4.dataview_scrm.sc_tprc_product_info e on e.product_id = b.product_id and e.shop_id = c.seller_id where d.valid_state in (100,200) and d.complete_state = 1 and c.order_mode = 10 union ALL select '交易管理系统' label,date(t6.close_ymd) close_ymd,t4.product_name,t4.product_id, t1.order_id,(t1.order_hosted_price-t1.order_refund_price) amt, (case when t1.order_mode <> 11 then t7.user_amount when t1.order_mode = 11 and t4.product2_type = 1 and t5.shop_id is not null then t5.price else t8.cost end) cost from hive.bdc_dwd.dw_mk_order t1 left join hive.bdc_dwd.dw_mk_order_business t2 on t1.order_id = t2.order_id and t2.acct_day=substring(cast(DATE_ADD('day',-1,CURRENT_DATE) as varchar),9,2) left join mysql7.dataview_trade.mk_order_merchant t3 on t1.order_id = t3.order_id left join mysql7.dataview_trade.ddc_product_info t4 on t4.product_id = t3.MERCHANT_ID and t4.snapshot_version = 'SELLING' left join mysql4.dataview_scrm.sc_tprc_product_info t5 on t5.product_id = t4.product_id and t5.shop_id = t1.seller_id left join hive.bdc_dwd.dw_fact_task_ss_daily t6 on t6.task_id = t2.task_id and t6.acct_time=date_format(date_add('day',-1,current_date),'%Y-%m-%d') left join (select a.task_id,sum(a.user_amount) user_amount from hive.bdc_dwd.dw_fn_deal_asyn_order a where a.is_new=1 and a.service='Trade_Payment' and a.state=1 and a.acct_day=substring(cast(DATE_ADD('day',-1,CURRENT_DATE) as varchar),9,2) group by a.task_id)t7 on t7.task_id = t2.task_id left join (select t1.par_order_id,sum(t1.order_hosted_price - t1.order_refund_price) cost from hive.bdc_dwd.dw_mk_order t1 where t1.acct_day = substring(cast(DATE_ADD('day',-1,CURRENT_DATE) as varchar),9,2) and t1.order_type = 1 and t1.order_stype = 4 and t1.order_mode = 12 group by t1.par_order_id) t8 on t1.order_id = t8.par_order_id where t1.acct_day = substring(cast(DATE_ADD('day',-1,CURRENT_DATE) as varchar),9,2) and t1.order_type = 1 and t1.order_stype in (4,5) and t1.order_mode <> 12 and t4.product_id is not null and t1.order_hosted_price > 0 and t6.is_deal = 1 and t6.close_ymd >= '2018-12-31' )a left join mysql7.dataview_trade.ddc_product_info b on a.product_id = b.product_id and b.snapshot_version = 'SELLING' where b.product2_type = 1 -------标品 and close_ymd between DATE_ADD('day',-7,CURRENT_DATE) and DATE_ADD('day',-1,CURRENT_DATE) GROUP BY b.product_name, b.selling_price_max, b.gross_profit_rate_max/100, b.actrul_supply_num, case when b.business_type =1 then '自营消化' when b.business_type =2 then '服务商消化' end order by count(a.order_id) desc limit 10""" sql_extractor = SqlExtractor(sql) print(sql_extractor.sql) print(sql_extractor.tables)
输出结果:
{'mysql8.dataview_tprc.tprc_task', 'hive.bdc_dwd.dw_mk_order', 'mysql4.dataview_fenxiao.fx_order_task', 'mysql4.dataview_fenxiao.fx_order', 'hive.bdc_dwd.dw_mk_order_business', 'mysql7.dataview_trade.mk_order_merchant', 'mysql4.dataview_scrm.sc_tprc_product_info', 'hive.bdc_dwd.dw_fn_deal_asyn_order', 'hive.bdc_dwd.dw_fact_task_ss_daily', 'mysql7.dataview_trade.ddc_product_info', 'hive.bdc_dwd.dw_mk_order_status'}
格式化结果:
SELECT b.product_name "产品", count(a.order_id) "订单量", b.selling_price_max "销售价", b.gross_profit_rate_max/100 "毛利率", CASE WHEN b.business_type =1 THEN '自营消化' WHEN b.business_type =2 THEN '服务商消化' END "消化模式" from (SELECT 'CRM签单' label,date(d.update_ymd) close_ymd,c.product_name,c.product_id, a.order_id,cast(a.recipient_amount AS DOUBLE) amt,d.cost FROM mysql4.dataview_fenxiao.fx_order a LEFT JOIN mysql4.dataview_fenxiao.fx_order_task b ON a.order_id = b.order_id LEFT JOIN mysql7.dataview_trade.ddc_product_info c ON cast(c.product_id AS varchar) = a.product_ids AND c.snapshot_version = 'SELLING' INNER JOIN (SELECT t1.par_order_id,max(t1.update_ymd) update_ymd, sum(CASE WHEN t4.product2_type = 1 AND t5.shop_id IS NOT NULL THEN t5.price ELSE t1.order_hosted_price END) cost FROM hive.bdc_dwd.dw_mk_order t1 LEFT JOIN hive.bdc_dwd.dw_mk_order_status t2 ON t1.order_id = t2.order_id AND t2.acct_day = substring(cast(DATE_ADD('day',-1,CURRENT_DATE) AS varchar),9,2) LEFT JOIN mysql7.dataview_trade.mk_order_merchant t3 ON t1.order_id = t3.order_id LEFT JOIN mysql7.dataview_trade.ddc_product_info t4 ON t4.product_id = t3.MERCHANT_ID AND t4.snapshot_version = 'SELLING' LEFT JOIN mysql4.dataview_scrm.sc_tprc_product_info t5 ON t5.product_id = t4.product_id AND t5.shop_id = t1.seller_id WHERE t1.acct_day = substring(cast(DATE_ADD('day',-1,CURRENT_DATE) AS varchar),9,2) AND t2.valid_state IN (100,200)------有效订单 AND t1.order_mode = 10 --------产品消耗订单 AND t2.complete_state = 1 -----订单已经完成 GROUP BY t1.par_order_id ) d ON d.par_order_id = b.task_order_id WHERE c.product_type = 0 AND date(from_unixtime(a.last_recipient_time)) > date('2016-01-01') AND a.payee_type <> 1 -----------已收款 UNION ALL SELECT '企业管家消耗' label,date(c.update_ymd) close_ymd,b.product_name,b.product_id, a.task_id,(CASE WHEN a.yb_price = 0 AND b.product2_type = 1 THEN b.selling_price_min ELSE a.yb_price END) amt, (CASE WHEN a.yb_price = 0 AND b.product2_type = 2 THEN 0 WHEN b.product2_type = 1 AND e.shop_id IS NOT NULL THEN e.price ELSE c.order_hosted_price END) cost FROM mysql8.dataview_tprc.tprc_task a LEFT JOIN mysql7.dataview_trade.ddc_product_info b ON a.product_id = b.product_id AND b.snapshot_version = 'SELLING' INNER JOIN hive.bdc_dwd.dw_mk_order c ON a.order_id = c.order_id AND c.acct_day = substring(cast(DATE_ADD('day',-1,CURRENT_DATE) AS varchar),9,2) LEFT JOIN hive.bdc_dwd.dw_mk_order_status d ON d.order_id = c.order_id AND d.acct_day = substring(cast(DATE_ADD('day',-1,CURRENT_DATE) AS varchar),9,2) LEFT JOIN mysql4.dataview_scrm.sc_tprc_product_info e ON e.product_id = b.product_id AND e.shop_id = c.seller_id WHERE d.valid_state IN (100,200) AND d.complete_state = 1 AND c.order_mode = 10 UNION ALL SELECT '交易管理系统' label,date(t6.close_ymd) close_ymd,t4.product_name,t4.product_id, t1.order_id,(t1.order_hosted_price-t1.order_refund_price) amt, (CASE WHEN t1.order_mode <> 11 THEN t7.user_amount WHEN t1.order_mode = 11 AND t4.product2_type = 1 AND t5.shop_id IS NOT NULL THEN t5.price ELSE t8.cost END) cost FROM hive.bdc_dwd.dw_mk_order t1 LEFT JOIN hive.bdc_dwd.dw_mk_order_business t2 ON t1.order_id = t2.order_id AND t2.acct_day=substring(cast(DATE_ADD('day',-1,CURRENT_DATE) AS varchar),9,2) LEFT JOIN mysql7.dataview_trade.mk_order_merchant t3 ON t1.order_id = t3.order_id LEFT JOIN mysql7.dataview_trade.ddc_product_info t4 ON t4.product_id = t3.MERCHANT_ID AND t4.snapshot_version = 'SELLING' LEFT JOIN mysql4.dataview_scrm.sc_tprc_product_info t5 ON t5.product_id = t4.product_id AND t5.shop_id = t1.seller_id LEFT JOIN hive.bdc_dwd.dw_fact_task_ss_daily t6 ON t6.task_id = t2.task_id AND t6.acct_time=date_format(date_add('day',-1,CURRENT_DATE),'%Y-%m-%d') LEFT JOIN (SELECT a.task_id,sum(a.user_amount) user_amount FROM hive.bdc_dwd.dw_fn_deal_asyn_order a WHERE a.is_new=1 AND a.service='Trade_Payment' AND a.state=1 AND a.acct_day=substring(cast(DATE_ADD('day',-1,CURRENT_DATE) AS varchar),9,2) GROUP BY a.task_id)t7 ON t7.task_id = t2.task_id LEFT JOIN (SELECT t1.par_order_id,sum(t1.order_hosted_price - t1.order_refund_price) cost FROM hive.bdc_dwd.dw_mk_order t1 WHERE t1.acct_day = substring(cast(DATE_ADD('day',-1,CURRENT_DATE) AS varchar),9,2) AND t1.order_type = 1 AND t1.order_stype = 4 AND t1.order_mode = 12 GROUP BY t1.par_order_id) t8 ON t1.order_id = t8.par_order_id WHERE t1.acct_day = substring(cast(DATE_ADD('day',-1,CURRENT_DATE) AS varchar),9,2) AND t1.order_type = 1 AND t1.order_stype IN (4,5) AND t1.order_mode <> 12 AND t4.product_id IS NOT NULL AND t1.order_hosted_price > 0 AND t6.is_deal = 1 AND t6.close_ymd >= '2018-12-31' )a LEFT JOIN mysql7.dataview_trade.ddc_product_info b ON a.product_id = b.product_id AND b.snapshot_version = 'SELLING' WHERE b.product2_type = 1 -------标品 AND close_ymd BETWEEN DATE_ADD('day',-7,CURRENT_DATE) AND DATE_ADD('day',-1,CURRENT_DATE) GROUP BY b.product_name, b.selling_price_max, b.gross_profit_rate_max/100, b.actrul_supply_num, CASE WHEN b.business_type =1 THEN '自营消化' WHEN b.business_type =2 THEN '服务商消化' END ORDER BY count(a.order_id) DESC LIMIT 10
以上这篇python如何解析复杂sql,实现数据库和表的提取的实例剖析就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持。
《魔兽世界》大逃杀!60人新游玩模式《强袭风暴》3月21日上线
暴雪近日发布了《魔兽世界》10.2.6 更新内容,新游玩模式《强袭风暴》即将于3月21 日在亚服上线,届时玩家将前往阿拉希高地展开一场 60 人大逃杀对战。
艾泽拉斯的冒险者已经征服了艾泽拉斯的大地及遥远的彼岸。他们在对抗世界上最致命的敌人时展现出过人的手腕,并且成功阻止终结宇宙等级的威胁。当他们在为即将于《魔兽世界》资料片《地心之战》中来袭的萨拉塔斯势力做战斗准备时,他们还需要在熟悉的阿拉希高地面对一个全新的敌人──那就是彼此。在《巨龙崛起》10.2.6 更新的《强袭风暴》中,玩家将会进入一个全新的海盗主题大逃杀式限时活动,其中包含极高的风险和史诗级的奖励。
《强袭风暴》不是普通的战场,作为一个独立于主游戏之外的活动,玩家可以用大逃杀的风格来体验《魔兽世界》,不分职业、不分装备(除了你在赛局中捡到的),光是技巧和战略的强弱之分就能决定出谁才是能坚持到最后的赢家。本次活动将会开放单人和双人模式,玩家在加入海盗主题的预赛大厅区域前,可以从强袭风暴角色画面新增好友。游玩游戏将可以累计名望轨迹,《巨龙崛起》和《魔兽世界:巫妖王之怒 经典版》的玩家都可以获得奖励。
更新动态
- 凤飞飞《我们的主题曲》飞跃制作[正版原抓WAV+CUE]
- 刘嘉亮《亮情歌2》[WAV+CUE][1G]
- 红馆40·谭咏麟《歌者恋歌浓情30年演唱会》3CD[低速原抓WAV+CUE][1.8G]
- 刘纬武《睡眠宝宝竖琴童谣 吉卜力工作室 白噪音安抚》[320K/MP3][193.25MB]
- 【轻音乐】曼托凡尼乐团《精选辑》2CD.1998[FLAC+CUE整轨]
- 邝美云《心中有爱》1989年香港DMIJP版1MTO东芝首版[WAV+CUE]
- 群星《情叹-发烧女声DSD》天籁女声发烧碟[WAV+CUE]
- 刘纬武《睡眠宝宝竖琴童谣 吉卜力工作室 白噪音安抚》[FLAC/分轨][748.03MB]
- 理想混蛋《Origin Sessions》[320K/MP3][37.47MB]
- 公馆青少年《我其实一点都不酷》[320K/MP3][78.78MB]
- 群星《情叹-发烧男声DSD》最值得珍藏的完美男声[WAV+CUE]
- 群星《国韵飘香·贵妃醉酒HQCD黑胶王》2CD[WAV]
- 卫兰《DAUGHTER》【低速原抓WAV+CUE】
- 公馆青少年《我其实一点都不酷》[FLAC/分轨][398.22MB]
- ZWEI《迟暮的花 (Explicit)》[320K/MP3][57.16MB]