挂号补单功能的完善
This commit is contained in:
@@ -547,47 +547,21 @@ function formatDateTime(date) {
|
|||||||
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
|
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 计算流水号:格式为 YYYYMMDD-XXX,其中 XXX 为后端按"科室+医生+当日"自增的 displayOrder
|
// 计算流水号:直接使用挂号记录表的主键ID(encounterId)
|
||||||
// 确保同一科室同一医生同一天内是 001、002、003... 递增
|
|
||||||
function calculateSerialNo(row) {
|
function calculateSerialNo(row) {
|
||||||
if (!row) {
|
if (!row) {
|
||||||
return '-';
|
return '-';
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取挂号日期(YYYYMMDD格式)
|
// 直接使用主键ID作为流水号
|
||||||
let dateStr = '';
|
if (row.encounterId != null && row.encounterId !== undefined) {
|
||||||
if (row.registerTime) {
|
return String(row.encounterId);
|
||||||
const date = new Date(row.registerTime);
|
} else if (row.id != null && row.id !== undefined) {
|
||||||
const year = date.getFullYear();
|
// 兼容其他可能的ID字段名
|
||||||
const month = String(date.getMonth() + 1).padStart(2, '0');
|
return String(row.id);
|
||||||
const day = String(date.getDate()).padStart(2, '0');
|
|
||||||
dateStr = `${year}${month}${day}`;
|
|
||||||
} else {
|
} else {
|
||||||
// 如果没有挂号时间,使用当前日期
|
return '-';
|
||||||
const now = new Date();
|
|
||||||
const year = now.getFullYear();
|
|
||||||
const month = String(now.getMonth() + 1).padStart(2, '0');
|
|
||||||
const day = String(now.getDate()).padStart(2, '0');
|
|
||||||
dateStr = `${year}${month}${day}`;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取序号部分(3位数字,001-999)
|
|
||||||
// 直接使用后端返回的 displayOrder,自增逻辑在后端按"科室+医生+当日"保证
|
|
||||||
let serialNum = 1;
|
|
||||||
if (row.displayOrder != null && row.displayOrder !== undefined) {
|
|
||||||
const num = Number(row.displayOrder) || 0;
|
|
||||||
serialNum = num > 0 ? num : 1;
|
|
||||||
} else if (row.serialNo) {
|
|
||||||
// 兼容旧数据:如果有已有的 serialNo 字段
|
|
||||||
const num = Number(row.serialNo) || 0;
|
|
||||||
serialNum = num > 0 ? num : 1;
|
|
||||||
} else {
|
|
||||||
// 兜底:没有任何序号信息时,给 1
|
|
||||||
serialNum = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 格式:YYYYMMDD-XXX(例如:20250113-001)
|
|
||||||
return `${dateStr}-${String(serialNum).padStart(3, '0')}`;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 提交补打挂号
|
// 提交补打挂号
|
||||||
|
|||||||
@@ -0,0 +1,378 @@
|
|||||||
|
<template>
|
||||||
|
<div class="cardiology-queue">
|
||||||
|
<div class="app-container">
|
||||||
|
<el-card>
|
||||||
|
<template #header>
|
||||||
|
<div class="card-header">
|
||||||
|
<span class="title">心内科分诊排队管理</span>
|
||||||
|
<div class="header-actions">
|
||||||
|
<el-button type="primary" @click="refreshData" :loading="loading">
|
||||||
|
<el-icon><Refresh /></el-icon>
|
||||||
|
刷新
|
||||||
|
</el-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<!-- 当前呼叫区 -->
|
||||||
|
<div class="current-call-section">
|
||||||
|
<div class="call-box">
|
||||||
|
<div class="call-text">
|
||||||
|
请 <span class="highlight">{{ currentCall?.number || '-' }}</span> 号
|
||||||
|
<span class="highlight">{{ currentCall?.name || '-' }}</span>
|
||||||
|
到 <span class="highlight">{{ currentCall?.room || '-' }}</span> 诊室就诊
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 候诊队列 -->
|
||||||
|
<div class="queue-section">
|
||||||
|
<h3 class="section-title">候诊队列</h3>
|
||||||
|
<el-table
|
||||||
|
:data="queueList"
|
||||||
|
v-loading="loading"
|
||||||
|
stripe
|
||||||
|
border
|
||||||
|
style="width: 100%"
|
||||||
|
:default-sort="{ prop: 'displayOrder', order: 'ascending' }"
|
||||||
|
>
|
||||||
|
<el-table-column type="index" label="序号" width="60" align="center" />
|
||||||
|
<el-table-column prop="patientName" label="患者姓名" width="120" align="center">
|
||||||
|
<template #default="scope">
|
||||||
|
{{ formatPatientName(scope.row.patientName) }}
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="practitionerName" label="医生" width="120" align="center" />
|
||||||
|
<el-table-column prop="room" label="诊室" width="100" align="center" />
|
||||||
|
<el-table-column prop="displayOrder" label="流水号" width="120" align="center">
|
||||||
|
<template #default="scope">
|
||||||
|
{{ formatSerialNo(scope.row) }}
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="status" label="状态" width="100" align="center">
|
||||||
|
<template #default="scope">
|
||||||
|
<el-tag :type="getStatusType(scope.row.status)">
|
||||||
|
{{ getStatusText(scope.row.status) }}
|
||||||
|
</el-tag>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="registerTime" label="挂号时间" width="180" align="center">
|
||||||
|
<template #default="scope">
|
||||||
|
{{ formatTime(scope.row.registerTime) }}
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="操作" width="150" align="center" fixed="right">
|
||||||
|
<template #default="scope">
|
||||||
|
<el-button
|
||||||
|
type="primary"
|
||||||
|
size="small"
|
||||||
|
@click="handleCall(scope.row)"
|
||||||
|
:disabled="scope.row.status === 'CALLED'"
|
||||||
|
>
|
||||||
|
叫号
|
||||||
|
</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
|
||||||
|
<!-- 分页 -->
|
||||||
|
<div class="pagination-wrapper">
|
||||||
|
<el-pagination
|
||||||
|
v-model:current-page="queryParams.pageNo"
|
||||||
|
v-model:page-size="queryParams.pageSize"
|
||||||
|
:page-sizes="[10, 20, 50, 100]"
|
||||||
|
:total="total"
|
||||||
|
layout="total, sizes, prev, pager, next, jumper"
|
||||||
|
@size-change="handleSizeChange"
|
||||||
|
@current-change="handlePageChange"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</el-card>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { ref, reactive, onMounted } from 'vue'
|
||||||
|
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||||
|
import { Refresh } from '@element-plus/icons-vue'
|
||||||
|
import { parseTime } from '@/utils/his'
|
||||||
|
import { getOutpatientRegistrationCurrent } from '@/views/charge/outpatientregistration/components/outpatientregistration'
|
||||||
|
|
||||||
|
// 响应式数据
|
||||||
|
const loading = ref(false)
|
||||||
|
const queueList = ref([])
|
||||||
|
const total = ref(0)
|
||||||
|
const currentCall = ref({
|
||||||
|
number: '-',
|
||||||
|
name: '-',
|
||||||
|
room: '-'
|
||||||
|
})
|
||||||
|
|
||||||
|
// 查询参数
|
||||||
|
const queryParams = reactive({
|
||||||
|
pageNo: 1,
|
||||||
|
pageSize: 20,
|
||||||
|
searchKey: '',
|
||||||
|
organizationId: null // 心内科科室ID,可以从路由参数或配置中获取
|
||||||
|
})
|
||||||
|
|
||||||
|
// 计算流水号:直接使用挂号记录表的主键ID(encounterId)
|
||||||
|
function formatSerialNo(row) {
|
||||||
|
if (!row) return '-'
|
||||||
|
|
||||||
|
// 直接使用主键ID作为流水号
|
||||||
|
if (row.encounterId != null && row.encounterId !== undefined) {
|
||||||
|
return String(row.encounterId)
|
||||||
|
} else if (row.id != null && row.id !== undefined) {
|
||||||
|
// 兼容其他可能的ID字段名
|
||||||
|
return String(row.id)
|
||||||
|
} else {
|
||||||
|
return '-'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 格式化患者姓名(脱敏)
|
||||||
|
function formatPatientName(name) {
|
||||||
|
if (!name || typeof name !== 'string') return '-'
|
||||||
|
if (name.length <= 2) return name.charAt(0) + '*'
|
||||||
|
return name.charAt(0) + '*' + name.slice(-1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 格式化时间
|
||||||
|
function formatTime(time) {
|
||||||
|
if (!time) return '-'
|
||||||
|
return parseTime(time, '{y}-{m}-{d} {h}:{i}:{s}')
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取状态类型
|
||||||
|
function getStatusType(status) {
|
||||||
|
const statusMap = {
|
||||||
|
'WAITING': 'info',
|
||||||
|
'CALLED': 'warning',
|
||||||
|
'IN_PROGRESS': 'success',
|
||||||
|
'COMPLETED': ''
|
||||||
|
}
|
||||||
|
return statusMap[status] || ''
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取状态文本
|
||||||
|
function getStatusText(status) {
|
||||||
|
const statusMap = {
|
||||||
|
'WAITING': '等待',
|
||||||
|
'CALLED': '已叫号',
|
||||||
|
'IN_PROGRESS': '就诊中',
|
||||||
|
'COMPLETED': '已完成'
|
||||||
|
}
|
||||||
|
return statusMap[status] || '未知'
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取队列数据
|
||||||
|
async function fetchQueueData() {
|
||||||
|
try {
|
||||||
|
loading.value = true
|
||||||
|
|
||||||
|
// 调用API获取当日就诊数据
|
||||||
|
const response = await getOutpatientRegistrationCurrent({
|
||||||
|
searchKey: queryParams.searchKey,
|
||||||
|
pageNo: queryParams.pageNo,
|
||||||
|
pageSize: queryParams.pageSize
|
||||||
|
})
|
||||||
|
|
||||||
|
if (response && response.code === 200) {
|
||||||
|
// 获取记录数据
|
||||||
|
const records = response.data?.records || response.data || []
|
||||||
|
|
||||||
|
// 过滤心内科的数据(如果有organizationId)
|
||||||
|
let filteredData = records
|
||||||
|
if (queryParams.organizationId) {
|
||||||
|
filteredData = records.filter(item => item.organizationId === queryParams.organizationId)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 转换为队列数据格式
|
||||||
|
queueList.value = filteredData.map(item => ({
|
||||||
|
id: item.encounterId,
|
||||||
|
patientName: item.patientName,
|
||||||
|
practitionerName: item.practitionerName || '-',
|
||||||
|
organizationName: item.organizationName,
|
||||||
|
room: item.organizationName || '-', // 使用科室名称作为诊室
|
||||||
|
displayOrder: item.displayOrder,
|
||||||
|
registerTime: item.registerTime,
|
||||||
|
status: 'WAITING' // 默认状态,实际应该从后端获取
|
||||||
|
}))
|
||||||
|
|
||||||
|
total.value = response.data?.total || filteredData.length || 0
|
||||||
|
} else {
|
||||||
|
queueList.value = []
|
||||||
|
total.value = 0
|
||||||
|
if (response && response.msg) {
|
||||||
|
ElMessage.warning('获取数据失败: ' + response.msg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取队列数据失败:', error)
|
||||||
|
ElMessage.error('获取队列数据失败: ' + (error.message || '未知错误'))
|
||||||
|
queueList.value = []
|
||||||
|
total.value = 0
|
||||||
|
} finally {
|
||||||
|
loading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 刷新数据
|
||||||
|
function refreshData() {
|
||||||
|
queryParams.pageNo = 1
|
||||||
|
fetchQueueData()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 叫号
|
||||||
|
async function handleCall(row) {
|
||||||
|
try {
|
||||||
|
await ElMessageBox.confirm(
|
||||||
|
`确认叫号:${formatPatientName(row.patientName)}(${formatSerialNo(row)})?`,
|
||||||
|
'确认叫号',
|
||||||
|
{
|
||||||
|
confirmButtonText: '确认',
|
||||||
|
cancelButtonText: '取消',
|
||||||
|
type: 'warning'
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
// TODO: 调用叫号API
|
||||||
|
// await callPatient(row.id)
|
||||||
|
|
||||||
|
// 更新当前呼叫信息
|
||||||
|
currentCall.value = {
|
||||||
|
number: row.displayOrder || '-',
|
||||||
|
name: formatPatientName(row.patientName),
|
||||||
|
room: row.room
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新状态
|
||||||
|
row.status = 'CALLED'
|
||||||
|
|
||||||
|
ElMessage.success('叫号成功')
|
||||||
|
|
||||||
|
// 刷新数据
|
||||||
|
await fetchQueueData()
|
||||||
|
} catch (error) {
|
||||||
|
if (error !== 'cancel') {
|
||||||
|
console.error('叫号失败:', error)
|
||||||
|
ElMessage.error('叫号失败: ' + (error.message || '未知错误'))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 分页大小改变
|
||||||
|
function handleSizeChange(val) {
|
||||||
|
queryParams.pageSize = val
|
||||||
|
queryParams.pageNo = 1
|
||||||
|
fetchQueueData()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 页码改变
|
||||||
|
function handlePageChange(val) {
|
||||||
|
queryParams.pageNo = val
|
||||||
|
fetchQueueData()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 组件挂载时获取数据
|
||||||
|
onMounted(() => {
|
||||||
|
fetchQueueData()
|
||||||
|
|
||||||
|
// 定时刷新数据(每30秒)
|
||||||
|
const refreshInterval = setInterval(() => {
|
||||||
|
fetchQueueData()
|
||||||
|
}, 30000)
|
||||||
|
|
||||||
|
// 组件卸载时清理定时器
|
||||||
|
return () => {
|
||||||
|
clearInterval(refreshInterval)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.cardiology-queue {
|
||||||
|
.app-container {
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
.title {
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-actions {
|
||||||
|
display: flex;
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.current-call-section {
|
||||||
|
margin: 20px 0;
|
||||||
|
text-align: center;
|
||||||
|
|
||||||
|
.call-box {
|
||||||
|
display: inline-block;
|
||||||
|
padding: 20px 40px;
|
||||||
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||||
|
border-radius: 12px;
|
||||||
|
box-shadow: 0 8px 20px rgba(102, 126, 234, 0.3);
|
||||||
|
animation: pulse 2s infinite;
|
||||||
|
|
||||||
|
.call-text {
|
||||||
|
font-size: 24px;
|
||||||
|
font-weight: bold;
|
||||||
|
color: white;
|
||||||
|
letter-spacing: 2px;
|
||||||
|
|
||||||
|
.highlight {
|
||||||
|
color: #ffd700;
|
||||||
|
font-size: 28px;
|
||||||
|
text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.queue-section {
|
||||||
|
margin-top: 20px;
|
||||||
|
|
||||||
|
.section-title {
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #333;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
padding-left: 10px;
|
||||||
|
border-left: 4px solid #667eea;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination-wrapper {
|
||||||
|
margin-top: 20px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes pulse {
|
||||||
|
0% {
|
||||||
|
box-shadow: 0 0 0 0 rgba(102, 126, 234, 0.4);
|
||||||
|
}
|
||||||
|
70% {
|
||||||
|
box-shadow: 0 0 0 15px rgba(102, 126, 234, 0);
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
box-shadow: 0 0 0 0 rgba(102, 126, 234, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
116
sql/query_encounter_id.sql
Normal file
116
sql/query_encounter_id.sql
Normal file
@@ -0,0 +1,116 @@
|
|||||||
|
-- 查询挂号记录表主键的SQL语句
|
||||||
|
-- 表名:adm_encounter
|
||||||
|
-- 主键字段:id (bigint,雪花算法生成)
|
||||||
|
|
||||||
|
-- ========================================
|
||||||
|
-- 1. 查询所有挂号记录的主键(简单查询)
|
||||||
|
-- ========================================
|
||||||
|
SELECT id
|
||||||
|
FROM adm_encounter
|
||||||
|
WHERE delete_flag = '0'
|
||||||
|
ORDER BY id DESC;
|
||||||
|
|
||||||
|
-- ========================================
|
||||||
|
-- 2. 查询最近N条挂号记录的主键
|
||||||
|
-- ========================================
|
||||||
|
SELECT id
|
||||||
|
FROM adm_encounter
|
||||||
|
WHERE delete_flag = '0'
|
||||||
|
ORDER BY start_time DESC
|
||||||
|
LIMIT 10;
|
||||||
|
|
||||||
|
-- ========================================
|
||||||
|
-- 3. 查询指定日期范围内的挂号记录主键
|
||||||
|
-- ========================================
|
||||||
|
SELECT id
|
||||||
|
FROM adm_encounter
|
||||||
|
WHERE delete_flag = '0'
|
||||||
|
AND DATE(start_time) >= '2026-01-01'
|
||||||
|
AND DATE(start_time) <= '2026-01-31'
|
||||||
|
ORDER BY start_time DESC;
|
||||||
|
|
||||||
|
-- ========================================
|
||||||
|
-- 4. 查询指定科室的挂号记录主键
|
||||||
|
-- ========================================
|
||||||
|
SELECT id
|
||||||
|
FROM adm_encounter
|
||||||
|
WHERE delete_flag = '0'
|
||||||
|
AND organization_id = 123 -- 替换为实际的科室ID
|
||||||
|
ORDER BY start_time DESC;
|
||||||
|
|
||||||
|
-- ========================================
|
||||||
|
-- 5. 查询指定患者的挂号记录主键
|
||||||
|
-- ========================================
|
||||||
|
SELECT id
|
||||||
|
FROM adm_encounter
|
||||||
|
WHERE delete_flag = '0'
|
||||||
|
AND patient_id = 456 -- 替换为实际的患者ID
|
||||||
|
ORDER BY start_time DESC;
|
||||||
|
|
||||||
|
-- ========================================
|
||||||
|
-- 6. 查询主键及基本信息(常用)
|
||||||
|
-- ========================================
|
||||||
|
SELECT
|
||||||
|
id AS 主键ID,
|
||||||
|
bus_no AS 就诊编码,
|
||||||
|
patient_id AS 患者ID,
|
||||||
|
organization_id AS 科室ID,
|
||||||
|
registrar_id AS 医生ID,
|
||||||
|
display_order AS 流水号序号,
|
||||||
|
start_time AS 挂号时间,
|
||||||
|
status_enum AS 状态
|
||||||
|
FROM adm_encounter
|
||||||
|
WHERE delete_flag = '0'
|
||||||
|
ORDER BY start_time DESC
|
||||||
|
LIMIT 20;
|
||||||
|
|
||||||
|
-- ========================================
|
||||||
|
-- 7. 统计主键数量(按日期分组)
|
||||||
|
-- ========================================
|
||||||
|
SELECT
|
||||||
|
DATE(start_time) AS 日期,
|
||||||
|
COUNT(id) AS 挂号记录数,
|
||||||
|
MIN(id) AS 最小主键ID,
|
||||||
|
MAX(id) AS 最大主键ID
|
||||||
|
FROM adm_encounter
|
||||||
|
WHERE delete_flag = '0'
|
||||||
|
GROUP BY DATE(start_time)
|
||||||
|
ORDER BY 日期 DESC
|
||||||
|
LIMIT 30;
|
||||||
|
|
||||||
|
-- ========================================
|
||||||
|
-- 8. 查询主键的最大值、最小值、总数
|
||||||
|
-- ========================================
|
||||||
|
SELECT
|
||||||
|
COUNT(*) AS 总记录数,
|
||||||
|
MIN(id) AS 最小主键ID,
|
||||||
|
MAX(id) AS 最大主键ID,
|
||||||
|
MAX(id) - MIN(id) AS 主键ID范围
|
||||||
|
FROM adm_encounter
|
||||||
|
WHERE delete_flag = '0';
|
||||||
|
|
||||||
|
-- ========================================
|
||||||
|
-- 9. 根据主键查询单条记录(精确查询)
|
||||||
|
-- ========================================
|
||||||
|
SELECT *
|
||||||
|
FROM adm_encounter
|
||||||
|
WHERE id = 1996923066055286785 -- 替换为实际的主键ID
|
||||||
|
AND delete_flag = '0';
|
||||||
|
|
||||||
|
-- ========================================
|
||||||
|
-- 10. 查询主键及关联的患者信息
|
||||||
|
-- ========================================
|
||||||
|
SELECT
|
||||||
|
e.id AS 挂号记录主键ID,
|
||||||
|
e.bus_no AS 就诊编码,
|
||||||
|
e.display_order AS 流水号序号,
|
||||||
|
e.start_time AS 挂号时间,
|
||||||
|
p.id AS 患者ID,
|
||||||
|
p.name AS 患者姓名,
|
||||||
|
p.bus_no AS 患者业务号
|
||||||
|
FROM adm_encounter e
|
||||||
|
INNER JOIN adm_patient p ON e.patient_id = p.id AND p.delete_flag = '0'
|
||||||
|
WHERE e.delete_flag = '0'
|
||||||
|
ORDER BY e.start_time DESC
|
||||||
|
LIMIT 20;
|
||||||
|
|
||||||
161
sql/sequence_explanation.md
Normal file
161
sql/sequence_explanation.md
Normal file
@@ -0,0 +1,161 @@
|
|||||||
|
# PostgreSQL SEQUENCE(序列)详解
|
||||||
|
|
||||||
|
## 语句解析
|
||||||
|
|
||||||
|
```sql
|
||||||
|
DROP SEQUENCE IF EXISTS "public"."adm_encounter_id_seq";
|
||||||
|
CREATE SEQUENCE "public"."adm_encounter_id_seq"
|
||||||
|
INCREMENT 1
|
||||||
|
MINVALUE 1
|
||||||
|
MAXVALUE 99999999
|
||||||
|
START 200
|
||||||
|
CACHE 1;
|
||||||
|
```
|
||||||
|
|
||||||
|
## 逐行解释
|
||||||
|
|
||||||
|
### 1. `DROP SEQUENCE IF EXISTS "public"."adm_encounter_id_seq";`
|
||||||
|
- **作用**:如果序列已存在,先删除它
|
||||||
|
- **`IF EXISTS`**:如果序列不存在,不会报错,直接跳过
|
||||||
|
- **`"public"."adm_encounter_id_seq"`**:序列的完整名称
|
||||||
|
- `public`:模式(schema)名,默认模式
|
||||||
|
- `adm_encounter_id_seq`:序列名称
|
||||||
|
|
||||||
|
### 2. `CREATE SEQUENCE "public"."adm_encounter_id_seq"`
|
||||||
|
- **作用**:创建一个新的序列
|
||||||
|
- **序列名称**:`adm_encounter_id_seq`(用于 `adm_encounter` 表的主键自增)
|
||||||
|
|
||||||
|
### 3. `INCREMENT 1`
|
||||||
|
- **作用**:每次递增的步长
|
||||||
|
- **含义**:每次调用 `nextval()` 时,序列值增加 1
|
||||||
|
- **示例**:如果当前值是 200,下次调用 `nextval()` 返回 201
|
||||||
|
|
||||||
|
### 4. `MINVALUE 1`
|
||||||
|
- **作用**:序列的最小值
|
||||||
|
- **含义**:序列值不能小于 1
|
||||||
|
- **注意**:如果序列达到最小值后继续递减,会报错(除非设置了 `CYCLE`)
|
||||||
|
|
||||||
|
### 5. `MAXVALUE 99999999`
|
||||||
|
- **作用**:序列的最大值
|
||||||
|
- **含义**:序列值不能超过 99999999(8位数)
|
||||||
|
- **注意**:如果序列达到最大值后继续递增,会报错(除非设置了 `CYCLE`)
|
||||||
|
|
||||||
|
### 6. `START 200`
|
||||||
|
- **作用**:序列的起始值
|
||||||
|
- **含义**:序列从 200 开始
|
||||||
|
- **示例**:第一次调用 `nextval()` 返回 200,第二次返回 201,以此类推
|
||||||
|
|
||||||
|
### 7. `CACHE 1`
|
||||||
|
- **作用**:缓存大小
|
||||||
|
- **含义**:每次从数据库获取序列值时,预分配 1 个值到内存
|
||||||
|
- **说明**:
|
||||||
|
- `CACHE 1`:每次只缓存 1 个值(最安全,但性能较低)
|
||||||
|
- `CACHE 20`:每次缓存 20 个值(性能更好,但可能跳号)
|
||||||
|
- **注意**:如果数据库重启,缓存中未使用的序列值会丢失,导致跳号
|
||||||
|
|
||||||
|
## 使用示例
|
||||||
|
|
||||||
|
### 1. 在表定义中使用(自动自增)
|
||||||
|
```sql
|
||||||
|
CREATE TABLE "adm_encounter" (
|
||||||
|
"id" int8 NOT NULL DEFAULT nextval('adm_encounter_id_seq'::regclass),
|
||||||
|
...
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. 手动获取下一个值
|
||||||
|
```sql
|
||||||
|
-- 获取下一个序列值
|
||||||
|
SELECT nextval('adm_encounter_id_seq');
|
||||||
|
-- 返回:200(第一次调用)
|
||||||
|
|
||||||
|
SELECT nextval('adm_encounter_id_seq');
|
||||||
|
-- 返回:201(第二次调用)
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. 查看当前值(不递增)
|
||||||
|
```sql
|
||||||
|
SELECT currval('adm_encounter_id_seq');
|
||||||
|
-- 返回:当前序列值(不会增加)
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. 重置序列
|
||||||
|
```sql
|
||||||
|
-- 将序列重置为指定值
|
||||||
|
SELECT setval('adm_encounter_id_seq', 200);
|
||||||
|
```
|
||||||
|
|
||||||
|
## 实际应用场景
|
||||||
|
|
||||||
|
### 场景1:插入数据时自动生成ID
|
||||||
|
```sql
|
||||||
|
INSERT INTO adm_encounter (patient_id, organization_id, ...)
|
||||||
|
VALUES (123, 456, ...);
|
||||||
|
-- id 字段会自动使用序列的下一个值(如 200, 201, 202...)
|
||||||
|
```
|
||||||
|
|
||||||
|
### 场景2:手动指定ID(不推荐)
|
||||||
|
```sql
|
||||||
|
INSERT INTO adm_encounter (id, patient_id, ...)
|
||||||
|
VALUES (999, 123, ...);
|
||||||
|
-- 注意:如果手动插入的ID与序列值冲突,可能导致问题
|
||||||
|
```
|
||||||
|
|
||||||
|
## 注意事项
|
||||||
|
|
||||||
|
### 1. 序列与雪花算法的区别
|
||||||
|
- **序列**:简单的数字递增(1, 2, 3...)
|
||||||
|
- **雪花算法**:分布式ID生成算法(如:1996923066055286785)
|
||||||
|
- **当前项目**:虽然定义了序列,但实际使用的是雪花算法(`IdType.ASSIGN_ID`)
|
||||||
|
|
||||||
|
### 2. 序列跳号的原因
|
||||||
|
- 事务回滚:如果事务回滚,序列值不会回退
|
||||||
|
- 缓存:`CACHE > 1` 时,数据库重启可能导致跳号
|
||||||
|
- 手动插入:手动插入ID后,序列不会自动调整
|
||||||
|
|
||||||
|
### 3. 序列的优缺点
|
||||||
|
**优点**:
|
||||||
|
- 简单易用
|
||||||
|
- 性能好(预分配)
|
||||||
|
- 保证唯一性
|
||||||
|
|
||||||
|
**缺点**:
|
||||||
|
- 可能跳号
|
||||||
|
- 不适合分布式环境
|
||||||
|
- 最大值有限制
|
||||||
|
|
||||||
|
## 相关查询语句
|
||||||
|
|
||||||
|
### 查看序列信息
|
||||||
|
```sql
|
||||||
|
-- 查看序列的详细信息
|
||||||
|
SELECT
|
||||||
|
sequence_name AS 序列名称,
|
||||||
|
last_value AS 当前值,
|
||||||
|
start_value AS 起始值,
|
||||||
|
increment_by AS 递增步长,
|
||||||
|
max_value AS 最大值,
|
||||||
|
min_value AS 最小值,
|
||||||
|
cache_size AS 缓存大小
|
||||||
|
FROM information_schema.sequences
|
||||||
|
WHERE sequence_name = 'adm_encounter_id_seq';
|
||||||
|
```
|
||||||
|
|
||||||
|
### 查看序列的下一个值(不实际使用)
|
||||||
|
```sql
|
||||||
|
SELECT last_value, is_called
|
||||||
|
FROM adm_encounter_id_seq;
|
||||||
|
```
|
||||||
|
|
||||||
|
### 修改序列
|
||||||
|
```sql
|
||||||
|
-- 修改序列的起始值
|
||||||
|
ALTER SEQUENCE adm_encounter_id_seq RESTART WITH 1000;
|
||||||
|
|
||||||
|
-- 修改序列的最大值
|
||||||
|
ALTER SEQUENCE adm_encounter_id_seq MAXVALUE 999999999;
|
||||||
|
|
||||||
|
-- 修改序列的缓存大小
|
||||||
|
ALTER SEQUENCE adm_encounter_id_seq CACHE 20;
|
||||||
|
```
|
||||||
|
|
||||||
Reference in New Issue
Block a user