Tdengine在php开发过程中的超级表建设、子表插入等系列出入库方法源码

原创| 笔记| 一个月前| 阅读 15 次 | 0赞同 0反对

概述:我在做一个小型物联网应用时,使用Laravel和Thinkphp作为后端开发,结合官方网站信息和Yurun\TDEngine\TDEngineManager接口,制作了下述标准方法,,包含了建立连接、sql执行、创建表、创建子表、单条数据入库、单表多条数据入库。

我在做一个小型物联网应用时,使用Laravel和Thinkphp作为后端开发,结合官方网站信息和Yurun\TDEngine\TDEngineManager接口,制作了下述标准方法,以供有需要的朋友做开发:

PHP后端开发源码,包含了建立连接、sql执行、创建表、创建子表、单条数据入库、单表多条数据入库、多表多条数据入库等。

use Yurun\TDEngine\ClientConfig;
use Yurun\TDEngine\TDEngineManager;

class XXXX extends Model {

	/**
	 * 要考虑两个用例:分页查询和图标连续点位
	 *
	 * @param array $input
	 * @return void
	 * @author dmakecn@163.com
	 * @since 2023-12-06
	 */
	public function initialize($input = []) {
		if ($input) {
			switch ($input['act']) {
			case "initstable":
				// 初始化超级表
				return self::createStable();
			case "statDataCount":
				return self::statDataCount($input['start'], $input['end'], $input['table']);
			case "inputdb":
				// 给某个表插入数据
				return self::initData($input);
			case "input1dbs":
				// 给某个表插入多条数据
				return self::init1Datas($input);
			case "inputmdbs":
				// 给多张表插入多条数据
				return self::initsMDatas($input);
			default:
				// 查询某表的数据
				return self::sensorDataInit(self::type2db($input['act']), $input);
			}
		}
	}


	/**
	 * tdengine的查询工具
	 * 注意注意:这个东西是给admin用的,因为时序库主要是在图表中体现点位
	 * 所以原生的将分类、数据点分离是正确的,不用加工
	 * https://docs.taosdata.com/2.6/reference/connector/php/
	 *
	 * @param string $sql
	 * @return void
	 * @author dmakecn@163.com
	 * @since 2023-12-01
	 */
	private function tdconnector($sql = '', $datatype = 'lines') {
		// 增加名称为 test 的连接配置
		TDEngineManager::setClientConfig('test', new ClientConfig(config('extra.tdengine')));
		// 设置默认数据库为test
		TDEngineManager::setDefaultClientName('test');
		// 获取客户端对象(\Yurun\TDEngine\Client)
		$client = TDEngineManager::getClient();
		// 执行查询
		$result = $client->sql($sql);

		switch ($datatype) {
		case "lines":
			// 获取接口原始返回数据d
			return $result->getResponse();
		case "pages":
			// 获取分页数据中的数据段
			$keys = [];
			foreach ($result->getColumns() as $column) {
				$k = $column->getName();
				array_push($keys, $k);
			}

			// 获得数据集,组装成标准json格式
			$list = [];
			foreach ($result->getData() as $row) {
				$item = [];
				foreach ($keys as $kk) {
					$v = $row[$kk];
					if ($kk == 'ts') {
						$v = date("Y-m-d H:i:s", strtotime($v));
					}
					$item[$kk] = $v;
				}
				array_push($list, $item);
			}

			return $list;
		case "count":
			// 获取分页数据总数
			$result->getResponse();
			foreach ($result->getData() as $row) {
				return $row['total'];
			}
		default:
			return $result->getResponse();
		}
	}

	/**
	 * 时序库的2种基础用法:分页、图表折线
	 *
	 * @param [type] $table
	 * @param [type] $input
	 * @return void
	 * @author dmakecn@163.com
	 * @since 2023-12-06
	 */
	private function sensorDataInit($table, $input) {
		$sql  = '';
		$data = [];
		$cond = getCond(urldecode($input['c_d']));

		switch ($input['datatype'] ?: 'pages') {
		case "pages":
			//分页类型的查询
			$page  = $cond['p_i'];
			$limit = $cond['p_s'];
			$where = 'where 1=1';
			if ($cond && $cond != []) {
				foreach ($cond as $k => $v) {
					// 1. 排除
					if (in_array($k, ['datatype', 'p_i', 'p_s'])) {
						continue;
					}

					//1.1 当dev_id==0时就查全部
					if ($k == 'dev_id' && $v == 0) {
						continue;
					}

					// 2. 拼接where
					if ($k == 'start') {
						$where .= " and (ts between '" . date('Y-m-d H:i:s', $v) . "' and ";
					} else if ($k == 'end') {
						$where .= " '" . date('Y-m-d H:i:s', $v) . "' ) ";
					} else {
						$where .= " and $k='$v' ";
					}
				}
			}

			// 3. 构造sql
			$page     = $page ? $page : 1;
			$limit    = $limit ? $limit : 10;
			$pagefrom = $limit * ($page - 1);
			$sqllist  = "select * from $table $where order by ts desc limit $pagefrom,$limit";
			$sqltotal = "select count(1) as total from $table $where";
			// 4. 返回结果
			$data['list']  = self::tdconnector($sqllist, 'pages');
			$data['count'] = self::tdconnector($sqltotal, 'count');
			$data['page']  = $page;
			break;
		case "lines":
			//组装数据图表类型
			$start = $cond['start'];
			$end   = $cond['end'];
			$sql   = "select * from $table where ts between '" . date('Y-m-d H:i:s', $start) . "' and '" . date('Y-m-d H:i:s', $end) . "'";
			$data  = self::tdconnector($sql, 'lines');
			break;
		}

		return getJsonCode($data);
	}  /**
	 * 查询表的数据量
	 *
	 * @param [type] $start
	 * @param [type] $end
	 * @return void
	 * @author dmakecn@163.com
	 * @since 2024-03-31
	 */
	public function statDataCount($start, $end, $table = '') {

		if ($table == 'all') {
			$stables = 'SHOW STABLES;';
			$tables  = self::tdconnector($stables, '')['data'];
			$last    = end($tables);
			$sql     = '';
			foreach ($tables as $t) {
				$sql .= "(select count(1)as rows,'$t[0]' as tablename from $t[0] ) " . ($t == $last ? ';' : 'union');
			}
			return self::tdconnector($sql, '');
		}

		$table = self::strtype2db($table);

		return self::tdconnector("select count(1) as rows from $table where ts between '$start' and '$end'", '');
	}

	/**
	 * 一张表一个记录值实时登记
	 *
	 * @return void
	 * @author dmakecn@163.com
	 * @since 2025-02-18
	 */
	private function initData($input) {

		$db = self::strtype2db($input['type']);

		// 根据设备id创建子表
		$res = self::writeSubtable($db . '_' . $input['dev_id'], $db, $input['tag'], $input['values']);

		return getJsonCode($res);
	}

	/**
	 * 一张表多个记录值,含时间
	 *
	 * @param [type] $input
	 * @return void
	 * @author dmakecn@163.com
	 * @since 2025-02-18
	 */
	private function init1Datas($input) {
		$db = self::strtype2db($input['type']);

		// 根据设备id创建子表
		$res = self::writeOneTable($db . '_' . $input['dev_id'], $db, $input['tag'], $input['values']);

		return getJsonCode($res);
	}

	/**
	 * 多张表多个记录值【未完成】
	 *
	 * @param [type] $input
	 * @return void
	 * @author dmakecn@163.com
	 * @since 2025-02-18
	 */
	private function initsMDatas($input) {

		$items = json_decode($input['items']);

		$res = self::writeMultTables($items);

		return getJsonCode($res);
	}
	

	/**
	 * 初始化创建超级表
	 *
	 * @return void
	 * @author dmakecn@163.com
	 * @since 2023-12-25
	 */
	private function createStable() {
		// 这里字段不能用varchar、decimal等,要用binary(4n)和float,因此在设计的时候把位数想好,TAGS后面的是外键,用于定位子表
		// 下述分别为 电气火灾、土壤墒情、烟雾探测、交通运输、水体、危险气体检测的超级表生成,如有其他表生成可以参考
		// $example = 'CREATE STABLE IF NOT EXISTS iot_data_electric_fire (ts timestamp, data_id bigint, batch_id int, b1 int,b2 int,b3 int,b4 int,b5 int,b6 int,b7 int) TAGS (dev_id int,gateway_id int,project_id int);';
		// $example = 'CREATE STABLE  IF NOT EXISTS iot_data_land (ts timestamp, data_id bigint, batch_id int, j1 int,j2 int,j3 int,j4 int,j5 int,j6 int,j7 int) TAGS (dev_id int,gateway_id int,project_id int);';
		// $example = 'CREATE STABLE  IF NOT EXISTS iot_data_smoke (ts timestamp, data_id bigint, batch_id int, a1 int,a2 int,a3 int,a4 int,a5 int) TAGS (dev_id int,gateway_id int,project_id int);';
		// $example = 'CREATE STABLE  IF NOT EXISTS iot_data_transport (ts timestamp, data_id bigint, batch_id int, t1 int,t2 int,t3 int,t4 int,t5 int) TAGS (dev_id int,gateway_id int,project_id int);';
		// $example = 'CREATE STABLE  IF NOT EXISTS iot_data_water (ts timestamp, data_id bigint, batch_id int, i1 float,i2 float,i3 float,i4 float,i5 float,i6 float,i7 float) TAGS (dev_id int,gateway_id int,project_id int);';
		// $example = 'CREATE STABLE  IF NOT EXISTS iot_data_harmfulgas (ts timestamp, data_id bigint, batch_id int, h1 int,h2 int,h3 int,h4 int,h5 int,h6 int,h7 int) TAGS (dev_id int,gateway_id int,project_id int);';
		// return self::tdconnector($example, '');

		return getJsonCode(true, '本项目已初始化超级表');
	}

	/**
	 * 创建超级表的子表并插入数据
	 *
	 * @param [type] 子表的名
	 * @param [type] 超级表的名
	 * @param [type] 外键表示
	 * @param [type] 参数
	 * @return void
	 * @author dmakecn@163.com
	 * @since 2023-12-25
	 */
	private function writeSubtable($sname, $name, $tag, $value) {
		// 承接上一个function,d1001属于子表的别名,用英文和编号的好处是规范,using后面这一坨,就是上述的外键,我想应该是方便快速查询吧
		// 案例同时做了2件事:用超级表的结构,向tags的标识内写入了数据
		//举例: INSERT INTO d1001 USING STABLE VALUES (1538548684000, 10.2, 220, 0.23) (1538548696650, 10.3, 218, 0.25);

		$example = "INSERT INTO $sname USING $name TAGS ($tag) VALUES (NOW, $value);";

		return self::tdconnector($example, '');
	}

	/**
	 * 写入单表:一条记录不能超过 48KB,一条 SQL 语句总长度不能超过 1MB。
	 * https://docs.taosdata.com/develop/insert-data/sql-writing/
	 *
	 * @return void
	 * @author dmakecn@163.com
	 * @since 2023-12-25
	 */
	public function writeOneTable($sname, $name, $tag, $values) {
		// 承接上一个function,给子表写数据,属于直接写咯,tags就没有用到,随便写咯
		// $example = 'INSERT INTO d1001 VALUES (ts1, 10.2, 220, 0.23) (ts2, 10.3, 218, 0.25);';

		$example = "INSERT INTO $sname USING $name TAGS ($tag) VALUES $values;";

		return self::tdconnector($example, '');
	}

	/**
	 * 写入多表:一条记录不能超过 48KB,一条 SQL 语句总长度不能超过 1MB。
	 * https://docs.taosdata.com/develop/insert-data/sql-writing/
	 *
	 * @param array $items 每张表的sname,name,tag,values
	 * @return void
	 * @author dmakecn@163.com
	 * @since 2023-12-25
	 */
	public function writeMultTables($items) {
		$sql = "INSERT INTO";
		foreach ($items as $i) {
			$db     = self::strtype2db($i->type);
			$sname  = $db . '_' . $i->dev_id;
			$name   = $db;
			$tag    = $i->tag;
			$values = $i->values;
			$sql .= " $sname USING $name TAGS ($tag) VALUES $values";
		}
		$sql .= ";";die($sql);

		return self::tdconnector($sql, '');
		// 同时向多个子表写入数据
		// $example = 'INSERT INTO d1001 VALUES (ts1, 10.3, 219, 0.31) (ts2, 12.6, 218, 0.33) d1002 VALUES (ts3, 12.3, 221, 0.31);';
		// https://docs.taosdata.com/2.6/reference/connector/php/
	}
}

如果前端通过post接口调用可参考下述方式:


            // 47 初始化tdengine数据库表
            //  JsonAPI({ 'c': 'xxx', 'a': 'xxx', 'data': 'act=initstable'}, function (res) {
            //     console.log(res);
            // }, api);

            // 48 传值到某个tdengine表:一张表一个记录值实时登记
            //  JsonAPI({ 'c': 'xxx', 'a': 'xxx', 'data': 'act=inputdb&type=smoke&dev_id=1&tag=1,2,3&values=1,2,3,4,5,6,7'}, function (res) {
            //     console.log(res);
            // }, api);


            // 49 1个表传多个值带时间的
            //  JsonAPI({ 'c': 'xxx', 'a': 'xxx', 'data': "act=input1dbs&type=smoke&dev_id=1&tag=1,2,3&values=('2025-02-18 21:17:55.596',11,2,3,4,5,6,7)('2025-02-18 21:15:55.596',12,2,3,4,5,6,7)('2025-02-18 21:10:55.596',13,2,3,4,5,6,7)"}, function (res) {
            //     console.log(res);
            // }, api);


            // 50 多个表传多个值带时间的
            // var multdatas=[
            //     {type:'soil',dev_id:1,tag:'1,2,3',values:"('2025-02-11 21:17:55.596',11,11,1,2,3,4,5,6,7)('2025-02-12 21:15:55.696',12,12,1,2,3,4,5,6,7)('2025-02-13 21:10:55.996',13,13,1,2,3,4,5,6,7)"},
            //     {type:'water',dev_id:2,tag:'1,2,3',values:"('2025-02-14 21:17:55.597',11,11,1,2,3,4,5,6,7)('2025-02-15 21:15:55.697',12,12,1,2,3,4,5,6,7)('2025-02-16 21:10:55.997',13,13,1,2,3,4,5,6,7)"},
            //     {type:'harmfulgas',dev_id:3,tag:'1,2,3',values:"('2025-02-17 21:17:55.597',11,11,1,2,3,4,5,6,7)('2025-02-18 21:15:55.898',12,12,1,2,3,4,5,6,7)('2025-02-19 21:10:55.998',13,13,1,2,3,4,5,6,7)"}];

            //  JsonAPI({ 'c': 'xxx', 'a': 'xxx', 'data': "act=inputmdbs&items="+JSON.stringify(multdatas)}, function (res) {
            //     console.log(res);
            // }, api);

            
            // 51 查询tdengine中的数据条数 table all或者表type
             JsonAPI({ 'c': 'xxx', 'a': 'xxx', 'data': 'act=statDataCount&start=2025-1-1&end=2025-2-19&table=smoke'}, function (res) {
                console.log(res);
            }, api);
以上就是我使用php管理tdengine的实操代码,有需要的朋友自取。



DMAKE 点击这里下载本文PDF

本频道需要登陆后才能评论,请登录


暂无评论...