在工业制造、设备管理等业务场景中,我们经常遇到这样的痛点:设备有上下级关系、工艺流程呈树状结构,如何用SQL优雅地查询出完整的层级关系?
传统的表连接查询面对多层嵌套时显得力不从心,递归存储过程又过于复杂。今天就来揭秘SQL Server的CTE递归查询这个利器,让你轻松处理任何深度的树形数据结构!
无论你是要查询某个设备下的所有子设备,还是要追溯工艺流程的完整路径,本文将通过实战案例,让你彻底掌握CTE递归查询的精髓。
在实际业务中,设备层级可能有3层、5层,甚至更深。用传统JOIN方式需要写N个表连接,代码冗长且不灵活。
既要能从叶子节点向上追溯到根节点,又要能从根节点向下展开所有子节点,单一查询方式无法满足。
递归存储过程性能好但代码复杂,简单查询可读性强但性能差,需要找到平衡点。
首先创建设备层级表和工艺流程表:
SQL-- 创建设备层级表
CREATE TABLE Equipment (
EquipmentID INT PRIMARY KEY,
EquipmentName NVARCHAR(100),
ParentID INT,
Level INT,
CreateDate DATETIME DEFAULT GETDATE()
);
-- 插入测试数据
INSERT INTO Equipment VALUES
(1, '生产线A', NULL, 1, '2024-01-01'),
(2, '工作站A1', 1, 2, '2024-01-02'),
(3, '工作站A2', 1, 2, '2024-01-03'),
(4, '设备A1-1', 2, 3, '2024-01-04'),
(5, '设备A1-2', 2, 3, '2024-01-05'),
(6, '传感器A1-1-1', 4, 4, '2024-01-06'),
(7, '传感器A1-1-2', 4, 4, '2024-01-07');
-- 创建工艺流程表
CREATE TABLE ProcessFlow (
ProcessID INT PRIMARY KEY,
ProcessName NVARCHAR(100),
ParentProcessID INT,
Sequence INT,
Duration INT -- 工序耗时(分钟)
);
-- 插入工艺流程数据
INSERT INTO ProcessFlow VALUES
(1, '产品制造', NULL, 1, 0),
(2, '原料准备', 1, 1, 30),
(3, '加工处理', 1, 2, 60),
(4, '质量检测', 1, 3, 20),
(5, '物料投入', 2, 1, 10),
(6, '预处理', 2, 2, 20),
(7, '粗加工', 3, 1, 30),
(8, '精加工', 3, 2, 30);
场景:查询某个设备及其所有下级设备
SQL-- 查询设备ID=1的所有下级设备
WITH EquipmentHierarchy AS (
-- 锚点:找到起始设备
SELECT
EquipmentID,
EquipmentName,
ParentID,
Level,
0 as Depth,
CAST(EquipmentName AS NVARCHAR(500)) as HierarchyPath
FROM Equipment
WHERE EquipmentID = 1
UNION ALL
-- 递归:找到所有子设备
SELECT
e.EquipmentID,
e.EquipmentName,
e.ParentID,
e.Level,
eh.Depth + 1,
CAST(eh.HierarchyPath + ' -> ' + e.EquipmentName AS NVARCHAR(500))
FROM Equipment e
INNER JOIN EquipmentHierarchy eh ON e.ParentID = eh.EquipmentID
)
SELECT
EquipmentID,
REPLICATE(' ', Depth) + EquipmentName as TreeView,
Depth,
HierarchyPath
FROM EquipmentHierarchy
ORDER BY Depth, EquipmentID;

在C#图形编程领域,SkiaSharp作为.NET平台上的强大2D图形库,为开发者提供了丰富的图形处理能力。而旋转变换作为基础且常用的图形操作,在UI设计、动画效果和图像处理中扮演着重要角色。本文将深入探讨SkiaSharp中的旋转变换原理及实现方法,通过详细的代码示例帮助你掌握这一技能。
SkiaSharp是Google Skia图形引擎的C#/.NET绑定,提供跨平台的2D图形API。它被广泛应用于Xamarin.Forms、.NET MAUI、WPF等平台的图形渲染,支持矢量图形、文本渲染、图像处理等功能。
使用SkiaSharp需要安装以下NuGet包:
C#Install-Package SkiaSharp Install-Package SkiaSharp.Views.WindowsForms
旋转变换是指将图形或图像围绕某个点按照指定角度进行旋转的过程。在SkiaSharp中,旋转通常围绕指定的原点进行,角度以顺时针方向为正。
旋转变换在数学上通过旋转矩阵实现,这一操作可表示为:
Pythonx' = x·cos(θ) - y·sin(θ)
y' = x·sin(θ) + y·cos(θ)
其中,(x, y)是原始坐标,(x', y')是旋转后的坐标,θ是旋转角度。
最直接的旋转方法是使用SKCanvas的RotateDegrees()或RotateRadians()方法:
C#using SkiaSharp.Views.Desktop;
using SkiaSharp;
namespace AppRotate
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
SKControl skControl = new SKControl();
skControl.Dock = DockStyle.Fill;
skControl.PaintSurface += OnPaintSurface;
Controls.Add(skControl);
}
private void OnPaintSurface(object sender, SKPaintSurfaceEventArgs e)
{
// 获取画布
SKCanvas canvas = e.Surface.Canvas;
// 清除背景
canvas.Clear(SKColors.White);
// 创建画笔
using SKPaint paint = new SKPaint
{
Color = SKColors.Blue,
StrokeWidth = 5,
IsAntialias = true,
Style = SKPaintStyle.Stroke
};
// 保存当前画布状态
canvas.Save();
// 执行旋转变换 (45度,围绕点(100, 100))
canvas.Translate(100, 100); // 移动原点到旋转中心
canvas.RotateDegrees(45); // 旋转45度
canvas.Translate(-100, -100); // 移回原来位置
// 绘制一个矩形
canvas.DrawRect(50, 50, 100, 100, paint);
// 恢复画布状态
canvas.Restore();
// 使用不同颜色绘制一个未旋转的矩形作为对比
using SKPaint paint2 = new SKPaint
{
Color = SKColors.Red,
StrokeWidth = 5,
IsAntialias = true,
Style = SKPaintStyle.Stroke
};
canvas.DrawRect(200, 50, 100, 100, paint2);
}
}
}

还在为复杂的数据列表展示而头疼吗?每次需要显示表格数据时都要重新造轮子?作为WinForms开发中的核心控件,ListView不仅能优雅地处理各种列表展示需求,更是构建专业用户界面的基石。
许多C#开发者在使用ListView时往往停留在基础应用层面,错失了它的强大功能。本文将从实战角度出发,带你深度掌握ListView的高级特性,让你的应用界面瞬间专业起来!
ListView提供了5种灵活的显示模式,每种都有其独特的应用场景:
在工业4.0的浪潮下,越来越多的C#开发者需要构建工业监控系统。但很多人在项目中遇到这样的困扰:界面逻辑和业务逻辑耦合严重,代码维护困难,测试覆盖率低。今天,我将通过一个完整的工业设备监控系统案例,带你掌握MVP架构模式的精髓,让你的C#项目结构更清晰,代码更易维护!
本文将解决三个核心问题:如何设计清晰的MVP架构、如何实现实时数据更新、如何处理异步操作中的异常。不过这个模式在Winform中我应用极少,我记得的也就是以前写过一个通讯工具。
本文将解决三个核心问题:如何设计清晰的MVP架构、如何实现实时数据更新、如何处理异步操作中的异常。不过这个模式在Winform中我应用极少,我记得的也就是以前写过一个通讯工具。
在传统的WinForms开发中,我们经常会遇到以下问题:
MVP模式正是解决这些问题的利器!
MVP模式将应用程序分为三个核心组件:

你是否还在为项目中频繁的对象序列化操作拖慢系统性能而头疼?传统的JSON序列化在高并发场景下捉襟见肘,Protobuf配置复杂让人望而却步?今天我要为大家介绍一个C#序列化领域的"性能怪兽"——MemoryPack!
作为由微软MVP Yoshifumi Kawai开发的新一代序列化库,MemoryPack在保持极简API的同时,性能竟然比System.Text.Json快10-50倍!更令人兴奋的是,它支持版本容错、循环引用处理,还能与Unity完美兼容。
本文将通过实战案例,手把手教你如何在项目中应用MemoryPack,让你的应用性能实现质的飞跃!
在日常C#开发中,我们经常遇到这些序列化难题:
C#// 传统JSON序列化
var json = JsonSerializer.Serialize(data);
var bytes = Encoding.UTF8.GetBytes(json);
// MemoryPack:一行代码搞定
var bytes = MemoryPackSerializer.Serialize(data);
三大核心优势:
Bashdotnet add package MemoryPack
C#using MemoryPack;
namespace AppMemoryPack
{
[MemoryPackable]
public partial class UserInfo
{
public int Id { get; set; }
public string Name { get; set; }
public DateTime CreateTime { get; set; }
public List<string> Tags { get; set; }
}
class Program
{
static void Main()
{
var user = new UserInfo
{
Id = 1001,
Name = "张三",
CreateTime = DateTime.Now,
Tags = new List<string> { "开发者", "技术爱好者" }
};
// 序列化:对象 → 字节数组
byte[] bytes = MemoryPackSerializer.Serialize(user);
Console.WriteLine($"序列化后大小: {bytes.Length} bytes");
// 反序列化:字节数组 → 对象
var deserializedUser = MemoryPackSerializer.Deserialize<UserInfo>(bytes);
Console.WriteLine($"姓名: {deserializedUser.Name}, ID: {deserializedUser.Id}");
}
}
}
