代码的目标是选择月,SaleID,总计和增长。我可以显示Month,SaleID和Total,但无法正常使用Growth,因为它总是从第一行开始计算。我在做什么错了?
我尝试设置变量,模拟LAG(),PREV,CURRENT,NEXT以获取计算应使用的行,但不会注册本机函数。
CREATE VIEW SalesTemp
AS
SELECT
DATE_FORMAT(Sales.SaleDate, "%Y-%m") AS Month,
Sales.SaleID,
Sales.Total
FROM Sales
WHERE SaleDate BETWEEN '2018-04-00' AND '2040-00-00'
GROUP BY DATE_FORMAT(Sales.SaleDate, "%Y-%m");
SELECT * FROM SalesTemp;
DROP VIEW IF EXISTS PercentageGrowth;
CREATE VIEW PercentageGrowth
AS
SELECT
DATE_FORMAT(Sales.SaleDate, "%Y-%m") AS Month,
Sales.SaleID,
Sales.Total,
CONCAT(ROUND(((Sales.Total) - SalesTemp.Total) / (SELECT SalesTemp.Total FROM SalesTemp GROUP BY DATE_FORMAT(SalesTemp.Month, "%Y-%m")) * 100, 2), "%") AS Growth
FROM Sales, SalesTemp
GROUP BY DATE_FORMAT(Sales.SaleDate, "%Y-%m");
SELECT * FROM PercentageGrowth;
DROP VIEW PercentageGrowth;
DROP VIEW SalesTemp;
我希望它通过((newValue-oldValue)/ oldValue)的计算来显示公司的成长。
因为我无法链接图片,所以我会给出结果。我现在从SELECT中得到的是:
+--------------------------------------+
| Month | SaleID | Total | Growth |
| ------- | ------ | ------- | ------- |
| 2018-04 | 1 | 310.46 | 00.00% |
| 2018-05 | 3 | 2160.62 | 595.54% |
| 2018-06 | 6 | 1087.89 | 250.21% |
| 2018-07 | 14 | 2314.54 | 645.09% |
+--------------------------------------+
我要说:
+--------------------------------------+
| Month | SaleID | Total | Growth |
| ------- | ------ | ------- | ------- |
| 2018-04 | 1 | 310.46 | 00.00% |
| 2018-05 | 3 | 2160.62 | 595.54% |
| 2018-06 | 6 | 1087.89 | -49.64% |
| 2018-07 | 14 | 2314.54 | 112.76% |
+--------------------------------------+
答案 0 :(得分:0)
当前,您正在跨所有 SaleID 和所有格式化日期月份交叉连接表配对,这又由于不清楚的聚合而进一步受到影响。
假设您使用的是MySQL 8+,请考虑几个CTE,其中包括 import 'package:flutter/material.dart';
import 'dart:async';
import 'dart:convert';
import 'package:flutter_easyrefresh/easy_refresh.dart';
import 'package:http/http.dart' as http;
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
backgroundColor: Colors.white
),
home: DuelLeaderBoards(),
);
}
}
class DuelLeaderBoards extends StatefulWidget {
@override
_DuelLeaderBoardsState createState() => _DuelLeaderBoardsState();
}
class _DuelLeaderBoardsState extends State<DuelLeaderBoards> {
List<Entry> str = [];
GlobalKey<EasyRefreshState> _easyRefreshKey = new GlobalKey<EasyRefreshState>();
GlobalKey<RefreshHeaderState> _headerKey = new GlobalKey<RefreshHeaderState>();
GlobalKey<RefreshHeaderState> _connectorHeaderKey = new GlobalKey<RefreshHeaderState>();
GlobalKey<RefreshFooterState> _footerKey = new GlobalKey<RefreshFooterState>();
GlobalKey<RefreshFooterState> _connectorFooterKey = new GlobalKey<RefreshFooterState>();
Future<LeaderBoards> getLeaderBoards(start) async {
String apiURL = 'https://stats.quake.com/api/v2/Leaderboard?from=$start&board=duel&season=current';
final response = await http.get(apiURL);
if (response.statusCode == 200) {
final responseBody = leaderBoardsFromJson(response.body);
return responseBody;
} else {
throw Exception('Failed to load Data');
}
}
void updateLeaderBoardList(e) async {
setState(() {
str.addAll(e.entries);
});
}
@override
void initState() {
getLeaderBoards(0).then((onValue) => str = onValue.entries );
super.initState();
}
@override
Widget build(BuildContext context) {
Widget header = ClassicsHeader(
key: _headerKey,
refreshText: "pullToRefresh",
refreshReadyText: "releaseToRefresh",
refreshingText: "refreshing...",
refreshedText: "refreshed",
moreInfo: "updateAt",
bgColor: Colors.transparent,
textColor: Colors.white,
);
Widget footer = ClassicsFooter(
key: _footerKey,
loadHeight: 50.0,
loadText: "pushToLoad",
loadReadyText: "releaseToLoad",
loadingText: "loading",
loadedText: "loaded",
noMoreText: "Finished",
moreInfo: "updateAt",
bgColor: Colors.transparent,
textColor: Colors.white,
);
return FutureBuilder(
future: getLeaderBoards(0),
builder:
(BuildContext context, AsyncSnapshot<LeaderBoards> snapshot) {
if (!snapshot.hasData) {
return Center(
child: CircularProgressIndicator(),
);
} else {
return Builder(builder: (BuildContext context) {
return Center(
child: new EasyRefresh(
key: _easyRefreshKey,
behavior: ScrollOverBehavior(),
refreshHeader: ConnectorHeader(
key: _connectorHeaderKey,
header: header,
),
refreshFooter: ConnectorFooter(
key: _connectorFooterKey,
footer: footer,
),
child: CustomScrollView(
semanticChildCount: str.length,
slivers: <Widget>[
SliverList(
delegate: SliverChildListDelegate(<Widget>[header]),
),
SliverList(
delegate: SliverChildBuilderDelegate(
(context, index) {
return new Container(
height: 70.0,
child: Card(
child: new Text(
'${index+1}: ${str[index].userName}',
style: new TextStyle(fontSize: 18.0),
),
));
},
childCount: str.length,
)),
SliverList(
delegate: SliverChildListDelegate(<Widget>[footer]),
)
],
),
onRefresh: () async {
await new Future.delayed(const Duration(seconds: 0), () {
setState(() {});
});
},
loadMore: () async {
getLeaderBoards(str.length).then((onValue) => {
updateLeaderBoardList(onValue)
});
},
// loadMore: () async {
// await new Future.delayed(const Duration(seconds: 0), () {
// getLeaderBoards(str.length).then((onValue) => {
// updateLeaderBoardList(onValue)
// });
// });
// },
)
);
});
}
});
}
}
LeaderBoards leaderBoardsFromJson(String str) {
final jsonData = json.decode(str);
return LeaderBoards.fromJson(jsonData);
}
String leaderBoardsToJson(LeaderBoards data) {
final dyn = data.toJson();
return json.encode(dyn);
}
class LeaderBoards {
String boardType;
List<Entry> entries;
int totalEntries;
LeaderBoards({
this.boardType,
this.entries,
this.totalEntries,
});
factory LeaderBoards.fromJson(Map<String, dynamic> json) => new LeaderBoards(
boardType: json["boardType"] == null ? null : json["boardType"],
entries: json["entries"] == null ? null : new List<Entry>.from(json["entries"].map((x) => Entry.fromJson(x))),
totalEntries: json["totalEntries"] == null ? null : json["totalEntries"],
);
Map<String, dynamic> toJson() => {
"boardType": boardType == null ? null : boardType,
"entries": entries == null ? null : new List<dynamic>.from(entries.map((x) => x.toJson())),
"totalEntries": totalEntries == null ? null : totalEntries,
};
}
class Entry {
String userName;
int eloRating;
String profileIconId;
String namePlateId;
Entry({
this.userName,
this.eloRating,
this.profileIconId,
this.namePlateId,
});
factory Entry.fromJson(Map<String, dynamic> json) => new Entry(
userName: json["userName"] == null ? null : json["userName"],
eloRating: json["eloRating"] == null ? null : json["eloRating"],
profileIconId: json["profileIconId"] == null ? null : json["profileIconId"],
namePlateId: json["namePlateId"] == null ? null : json["namePlateId"],
);
Map<String, dynamic> toJson() => {
"userName": userName == null ? null : userName,
"eloRating": eloRating == null ? null : eloRating,
"profileIconId": profileIconId == null ? null : profileIconId,
"namePlateId": namePlateId == null ? null : namePlateId,
};
}
,相差一个月的总计:
LAG
对于MySQL 5.7或更低版本,请考虑子查询的自联接,该子查询显式联接 SaleID 和上个月中的任何日期,以将所有日期标准化为各自月的第一个月。
WITH cte1 AS
(SELECT DATE_FORMAT(Sales.SaleDate, "%Y-%m") AS `Month`,
Sales.SaleID,
SUM(Sales.Total) AS `Total_Sales`
FROM Sales
WHERE SaleDate BETWEEN '2018-04-00' AND '2040-00-00'
GROUP BY
DATE_FORMAT(Sales.SaleDate, "%Y-%m"),
Sales.SaleID
),
cte2 AS
(SELECT *,
LAG(`Total_Sales`) OVER (PARTITION BY `SaleID`
ORDER BY `Month`) AS `Lag_Total_Sales`
FROM cte1)
SELECT `Month`, `SaleID`, `Total_Sales`,
CONCAT(
ROUND(
(`Total_Sales` - `Lag_Total_Sales`) / `Lag_Total_Sales`
, 2) * 100
, '%') AS `Growth`
FROM cte2
Rextester Demo (MySQL 5.7版本)