mirror of
https://gitee.com/dromara/mayfly-go
synced 2025-11-03 16:00:25 +08:00
feat: mysql 支持编辑表结构、索引
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<el-dialog title="创建表" v-model="dialogVisible" :before-close="cancel" width="90%">
|
<el-dialog :title="title" v-model="dialogVisible" :before-close="cancel" width="90%">
|
||||||
<el-form label-position="left" ref="formRef" :model="tableData" label-width="80px">
|
<el-form label-position="left" ref="formRef" :model="tableData" label-width="80px">
|
||||||
<el-row>
|
<el-row>
|
||||||
<el-col :span="12">
|
<el-col :span="12">
|
||||||
@@ -13,24 +13,31 @@
|
|||||||
<el-input style="width: 80%" v-model="tableData.tableComment" size="small"></el-input>
|
<el-input style="width: 80%" v-model="tableData.tableComment" size="small"></el-input>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col style="margin-top: 20px" :span="12">
|
<el-col :span="12">
|
||||||
<el-form-item prop="characterSet" label="字符集">
|
<el-form-item prop="characterSet" label="charset">
|
||||||
<el-select filterable style="width: 80%" v-model="tableData.characterSet" size="small">
|
<el-select filterable style="width: 80%" v-model="tableData.characterSet" size="small">
|
||||||
<el-option v-for="item in characterSetNameList" :key="item" :label="item" :value="item"> </el-option>
|
<el-option v-for="item in characterSetNameList" :key="item" :label="item" :value="item"> </el-option>
|
||||||
</el-select>
|
</el-select>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item prop="characterSet" label="collation">
|
||||||
|
<el-select filterable style="width: 80%" v-model="tableData.collation" size="small">
|
||||||
|
<el-option v-for="item in collationNameList" :key="item" :label="tableData.characterSet+'_'+item" :value="tableData.characterSet+'_'+item"> </el-option>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
</el-row>
|
</el-row>
|
||||||
|
|
||||||
<el-tabs v-model="activeName">
|
<el-tabs v-model="activeName">
|
||||||
<el-tab-pane label="字段" name="1">
|
<el-tab-pane label="字段" name="1">
|
||||||
<el-table :data="tableData.fields.res">
|
<el-table :data="tableData.fields.res" :max-height="tableData.height">
|
||||||
<el-table-column :prop="item.prop" :label="item.label" v-for="item in tableData.fields.colNames" :key="item.prop">
|
<el-table-column :prop="item.prop" :label="item.label" v-for="item in tableData.fields.colNames" :key="item.prop">
|
||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
<el-input v-if="item.prop === 'name'" size="small" v-model="scope.row.name"></el-input>
|
<el-input v-if="item.prop === 'name'" size="small" v-model="scope.row.name"></el-input>
|
||||||
|
|
||||||
<el-select v-if="item.prop === 'type'" filterable size="small" v-model="scope.row.type">
|
<el-select v-if="item.prop === 'type'" filterable size="small" v-model="scope.row.type">
|
||||||
<el-option v-for="typeValue in typeList" :key="typeValue" :value="typeValue">{{ typeValue }}</el-option>
|
<el-option v-for="typeValue in columnTypeList" :key="typeValue" :value="typeValue">{{ typeValue }}</el-option>
|
||||||
</el-select>
|
</el-select>
|
||||||
|
|
||||||
<el-input v-if="item.prop === 'value'" size="small" v-model="scope.row.value"> </el-input>
|
<el-input v-if="item.prop === 'value'" size="small" v-model="scope.row.value"> </el-input>
|
||||||
@@ -45,14 +52,55 @@
|
|||||||
|
|
||||||
<el-input v-if="item.prop === 'remark'" size="small" v-model="scope.row.remark"> </el-input>
|
<el-input v-if="item.prop === 'remark'" size="small" v-model="scope.row.remark"> </el-input>
|
||||||
|
|
||||||
<el-button v-if="item.prop === 'action'" type="text" size="small" @click.prevent="deleteRow(scope.$index)">删除</el-button>
|
<el-link v-if="item.prop === 'action'" type="danger" plain size="small" :underline="false" @click.prevent="deleteRow(scope.$index)">删除</el-link>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
</el-table>
|
</el-table>
|
||||||
<el-row style="margin-top: 20px">
|
<el-row style="margin-top: 20px">
|
||||||
<el-button @click="addRow()" type="text" icon="plus"></el-button>
|
<el-button @click="addDefaultRows()" link type="warning" icon="plus">添加默认列</el-button>
|
||||||
|
<el-button @click="addRow()" link type="primary" icon="plus">添加列</el-button>
|
||||||
</el-row>
|
</el-row>
|
||||||
</el-tab-pane>
|
</el-tab-pane>
|
||||||
|
<el-tab-pane label="索引" name="2">
|
||||||
|
<el-table :data="tableData.indexs.res" :max-height="tableData.height">
|
||||||
|
<el-table-column :prop="item.prop" :label="item.label" v-for="item in tableData.indexs.colNames" :key="item.prop">
|
||||||
|
<template #default="scope">
|
||||||
|
|
||||||
|
<el-input v-if="item.prop === 'indexName'" size="small" v-model="scope.row.indexName"></el-input>
|
||||||
|
|
||||||
|
<el-select
|
||||||
|
v-if="item.prop === 'columnNames'"
|
||||||
|
v-model="scope.row.columnNames"
|
||||||
|
multiple
|
||||||
|
collapse-tags
|
||||||
|
collapse-tags-tooltip
|
||||||
|
filterable
|
||||||
|
placeholder="请选择字段"
|
||||||
|
style="width: 100%"
|
||||||
|
>
|
||||||
|
<el-option v-for="cl in tableData.indexs.columns" :key="cl.name" :label="cl.name" :value="cl.name" >
|
||||||
|
{{ cl.name + ' - ' + (cl.remark || '') }}
|
||||||
|
</el-option>
|
||||||
|
</el-select>
|
||||||
|
|
||||||
|
<el-checkbox v-if="item.prop === 'unique'" size="small" v-model="scope.row.unique"> </el-checkbox>
|
||||||
|
|
||||||
|
<el-select v-if="item.prop === 'indexType'" filterable size="small" v-model="scope.row.indexType">
|
||||||
|
<el-option v-for="typeValue in indexTypeList" :key="typeValue" :value="typeValue">{{ typeValue }}</el-option>
|
||||||
|
</el-select>
|
||||||
|
|
||||||
|
<el-input v-if="item.prop === 'indexComment'" size="small" v-model="scope.row.indexComment"> </el-input>
|
||||||
|
|
||||||
|
<el-link v-if="item.prop === 'action'" type="danger" plain size="small" :underline="false" @click.prevent="deleteIndex(scope.$index)">删除</el-link>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
|
||||||
|
<el-row style="margin-top: 20px">
|
||||||
|
<el-button @click="addIndex()" link type="primary" icon="plus">添加索引</el-button>
|
||||||
|
</el-row>
|
||||||
|
</el-tab-pane>
|
||||||
|
|
||||||
</el-tabs>
|
</el-tabs>
|
||||||
</el-form>
|
</el-form>
|
||||||
<template #footer>
|
<template #footer>
|
||||||
@@ -65,9 +113,10 @@
|
|||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { watch, toRefs, reactive, defineComponent, ref, getCurrentInstance } from 'vue';
|
import { watch, toRefs, reactive, defineComponent, ref, getCurrentInstance } from 'vue';
|
||||||
import { TYPE_LIST, CHARACTER_SET_NAME_LIST } from './service.ts';
|
import {TYPE_LIST, CHARACTER_SET_NAME_LIST, COLLATION_SUFFIX_LIST} from './service.ts';
|
||||||
import { ElMessage } from 'element-plus';
|
import { ElMessage } from 'element-plus';
|
||||||
import SqlExecBox from './component/SqlExecBox.ts';
|
import SqlExecBox from './component/SqlExecBox.ts';
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: 'createTable',
|
name: 'createTable',
|
||||||
props: {
|
props: {
|
||||||
@@ -90,12 +139,15 @@ export default defineComponent({
|
|||||||
setup(props: any, { emit }) {
|
setup(props: any, { emit }) {
|
||||||
const formRef: any = ref();
|
const formRef: any = ref();
|
||||||
const { proxy } = getCurrentInstance() as any;
|
const { proxy } = getCurrentInstance() as any;
|
||||||
|
|
||||||
const state = reactive({
|
const state = reactive({
|
||||||
dialogVisible: false,
|
dialogVisible: false,
|
||||||
btnloading: false,
|
btnloading: false,
|
||||||
activeName: '1',
|
activeName: '1',
|
||||||
typeList: TYPE_LIST,
|
columnTypeList: TYPE_LIST,
|
||||||
|
indexTypeList: ['BTREE'], // mysql索引类型详解 http://c.biancheng.net/view/7897.html
|
||||||
characterSetNameList: CHARACTER_SET_NAME_LIST,
|
characterSetNameList: CHARACTER_SET_NAME_LIST,
|
||||||
|
collationNameList: COLLATION_SUFFIX_LIST,
|
||||||
tableData: {
|
tableData: {
|
||||||
fields: {
|
fields: {
|
||||||
colNames: [
|
colNames: [
|
||||||
@@ -137,7 +189,6 @@ export default defineComponent({
|
|||||||
label: '操作',
|
label: '操作',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|
||||||
res: [
|
res: [
|
||||||
{
|
{
|
||||||
name: '',
|
name: '',
|
||||||
@@ -151,9 +202,49 @@ export default defineComponent({
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
indexs: {
|
||||||
|
colNames: [
|
||||||
|
{
|
||||||
|
prop: 'indexName',
|
||||||
|
label: '索引名',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
prop: 'columnNames',
|
||||||
|
label: '列名',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
prop: 'unique',
|
||||||
|
label: '唯一',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
prop: 'indexType',
|
||||||
|
label: '类型',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
prop: 'indexComment',
|
||||||
|
label: '备注',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
prop: 'action',
|
||||||
|
label: '操作',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
columns: [{name: '', remark:''}],
|
||||||
|
res: [
|
||||||
|
{
|
||||||
|
indexName: '',
|
||||||
|
columnNames: [],
|
||||||
|
unique: false,
|
||||||
|
indexType: 'BTREE',
|
||||||
|
indexComment: '',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
characterSet: 'utf8mb4',
|
characterSet: 'utf8mb4',
|
||||||
|
collation: 'utf8mb4_general_ci',
|
||||||
tableName: '',
|
tableName: '',
|
||||||
tableComment: '',
|
tableComment: '',
|
||||||
|
height: 550
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -176,45 +267,228 @@ export default defineComponent({
|
|||||||
remark: '',
|
remark: '',
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
const addIndex = () => {
|
||||||
|
state.tableData.indexs.res.push({
|
||||||
|
indexName: '',
|
||||||
|
columnNames: [],
|
||||||
|
unique: false,
|
||||||
|
indexType: 'BTREE',
|
||||||
|
indexComment: '',
|
||||||
|
});
|
||||||
|
};
|
||||||
|
const addDefaultRows = () => {
|
||||||
|
state.tableData.fields.res.push(
|
||||||
|
{name: 'id', type: 'bigint', length: '20', value: '', notNull: true, pri: true, auto_increment: true, remark: '主键ID'},
|
||||||
|
{name: 'creator_id', type: 'bigint', length: '20', value: '', notNull: true, pri: false, auto_increment: false, remark: '创建人id'},
|
||||||
|
{name: 'creator', type: 'varchar', length: '100', value: '', notNull: true, pri: false, auto_increment: false, remark: '创建人姓名'},
|
||||||
|
{name: 'creat_time', type: 'datetime', length: '', value: '', notNull: true, pri: false, auto_increment: false, remark: '创建时间'},
|
||||||
|
{name: 'updater_id', type: 'bigint', length: '20', value: '', notNull: true, pri: false, auto_increment: false, remark: '修改人id'},
|
||||||
|
{name: 'updater', type: 'varchar', length: '100', value: '', notNull: true, pri: false, auto_increment: false, remark: '修改人姓名'},
|
||||||
|
{name: 'update_time', type: 'datetime', length: '', value: '', notNull: true, pri: false, auto_increment: false, remark: '修改时间'},
|
||||||
|
);
|
||||||
|
};
|
||||||
const deleteRow = (index: any) => {
|
const deleteRow = (index: any) => {
|
||||||
state.tableData.fields.res.splice(index, 1);
|
state.tableData.fields.res.splice(index, 1);
|
||||||
};
|
};
|
||||||
|
const deleteIndex = (index: any) => {
|
||||||
|
state.tableData.indexs.res.splice(index, 1);
|
||||||
|
};
|
||||||
const submit = async () => {
|
const submit = async () => {
|
||||||
let data = state.tableData;
|
let sql = genSql();
|
||||||
let primary_key = '';
|
if(!sql){
|
||||||
let fields: string[] = [];
|
ElMessage.warning('没有更改');
|
||||||
data.fields.res.forEach((item) => {
|
return;
|
||||||
fields.push(
|
}
|
||||||
`${item.name} ${item.type}${+item.length > 0 ? `(${item.length})` : ''} ${item.notNull ? 'NOT NULL' : ''} ${
|
|
||||||
item.auto_increment ? 'AUTO_INCREMENT' : ''
|
|
||||||
} ${item.value ? 'DEFAULT ' + item.value : item.notNull ? '' : 'DEFAULT NULL'} ${
|
|
||||||
item.remark ? `COMMENT '${item.remark}'` : ''
|
|
||||||
} \n`
|
|
||||||
);
|
|
||||||
if (item.pri) {
|
|
||||||
primary_key += `${item.name},`;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
let sql = `
|
|
||||||
CREATE TABLE ${data.tableName} (
|
|
||||||
${fields.join(',')}
|
|
||||||
${primary_key ? `, PRIMARY KEY (${primary_key.slice(0, -1)})` : ''}
|
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=${data.characterSet} COLLATE=utf8mb4_bin COMMENT='${data.tableComment}';`;
|
|
||||||
|
|
||||||
SqlExecBox({
|
SqlExecBox({
|
||||||
sql: sql,
|
sql: sql,
|
||||||
dbId: props.dbId as any,
|
dbId: props.dbId as any,
|
||||||
db: props.db,
|
db: props.db,
|
||||||
runSuccessCallback: () => {
|
runSuccessCallback: () => {
|
||||||
ElMessage.success('创建成功');
|
proxy.$parent.openEditTable({ tableName: state.tableData.tableName });
|
||||||
proxy.$parent.tableInfo({ id: props.dbId });
|
// cancel();
|
||||||
cancel();
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 对比两个数组,取出被修改过的对象数组
|
||||||
|
* @param oldArr 原对象数组
|
||||||
|
* @param nowArr 修改后的对象数组
|
||||||
|
* @param key 标志对象唯一属性
|
||||||
|
*/
|
||||||
|
const filterChangedData = (oldArr: object[], nowArr: object[], key: string) : { del: any[], add: any[], upd: any[] } => {
|
||||||
|
let data = {
|
||||||
|
del: [] as object[], // 删除的数据
|
||||||
|
add: [] as object[], // 新增的数据
|
||||||
|
upd:[] as object[] // 修改的数据
|
||||||
|
}
|
||||||
|
|
||||||
|
// 旧数据为空
|
||||||
|
if(oldArr && Array.isArray(oldArr) && oldArr.length===0
|
||||||
|
&& nowArr && Array.isArray(nowArr) && nowArr.length>0 ){
|
||||||
|
data.add = nowArr;
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 新数据为空
|
||||||
|
if(nowArr && Array.isArray(nowArr) && nowArr.length===0
|
||||||
|
&& oldArr && Array.isArray(oldArr) && oldArr.length>0 ){
|
||||||
|
data.del = oldArr;
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
let oldMap= {}, newMap = {};
|
||||||
|
oldArr.forEach(a => oldMap[a[key]] = a)
|
||||||
|
|
||||||
|
nowArr.forEach(a => {
|
||||||
|
let k = a[key]
|
||||||
|
newMap[k] = a;
|
||||||
|
if(!oldMap.hasOwnProperty(k)){// 新增
|
||||||
|
data.add.push(a)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
oldArr.forEach(a=>{
|
||||||
|
let k = a[key];
|
||||||
|
let newData = newMap[k];
|
||||||
|
if(!newData){ // 删除
|
||||||
|
data.del.push(a)
|
||||||
|
}else{ // 判断每个字段是否相等,否则为修改
|
||||||
|
for(let f in a){
|
||||||
|
let oldV = a[f]
|
||||||
|
let newV = newData[f]
|
||||||
|
if(oldV.toString() !== newV.toString()){
|
||||||
|
data.upd.push(newData)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
const genSql = () => {
|
||||||
|
|
||||||
|
const genColumnBasicSql = (cl: any) => {
|
||||||
|
let val = cl.value ? (cl.value === 'CURRENT_TIMESTAMP' ? cl.value : '\'' + cl.value + '\'') : '';
|
||||||
|
let defVal = `${val ? ('DEFAULT ' + val) :''}`;
|
||||||
|
let length = cl.length?`(${cl.length})`:'';
|
||||||
|
return ` ${cl.name} ${cl.type}${length} ${cl.notNull?'NOT NULL':'NULL'} ${cl.auto_increment?'AUTO_INCREMENT':''} ${defVal} comment '${cl.remark||''}' `
|
||||||
|
}
|
||||||
|
|
||||||
|
let data = state.tableData;
|
||||||
|
// 创建表
|
||||||
|
if(!props.data.edit){
|
||||||
|
if(state.activeName === '1'){// 创建表结构
|
||||||
|
let primary_key = '';
|
||||||
|
let fields: string[] = [];
|
||||||
|
data.fields.res.forEach((item) => {
|
||||||
|
fields.push(genColumnBasicSql(item));
|
||||||
|
if (item.pri) {
|
||||||
|
primary_key += `${item.name},`;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return `CREATE TABLE ${data.tableName}
|
||||||
|
( ${fields.join(',')}
|
||||||
|
${primary_key ? `, PRIMARY KEY (${primary_key.slice(0, -1)})` : ''}
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=${data.characterSet} COLLATE =${data.collation} COMMENT='${data.tableComment}';`;
|
||||||
|
|
||||||
|
} else if (state.activeName === '2' && data.indexs.res.length > 0){ // 创建索引
|
||||||
|
let sql = `ALTER TABLE ${data.tableName}`;
|
||||||
|
state.tableData.indexs.res.forEach(a=>{
|
||||||
|
sql += ` ADD ${a.unique?'UNIQUE':''} INDEX ${a.indexName}(${a.columnNames.join(',')}) USING ${a.indexType} COMMENT '${a.indexComment}',`;
|
||||||
|
})
|
||||||
|
return sql.substring(0, sql.length - 1) + ';'
|
||||||
|
}
|
||||||
|
} else { // 修改
|
||||||
|
let addSql = '', updSql = '', delSql = '';
|
||||||
|
if(state.activeName === '1'){// 修改列
|
||||||
|
let changeData = filterChangedData(oldData.fields, state.tableData.fields.res, 'name')
|
||||||
|
if(changeData.add.length > 0){
|
||||||
|
addSql = `ALTER TABLE ${data.tableName}`
|
||||||
|
changeData.add.forEach(a=>{
|
||||||
|
addSql += ` ADD ${genColumnBasicSql(a)},`
|
||||||
|
})
|
||||||
|
addSql = addSql.substring(0, addSql.length - 1)
|
||||||
|
addSql +=';'
|
||||||
|
}
|
||||||
|
|
||||||
|
if(changeData.upd.length > 0){
|
||||||
|
updSql = `ALTER TABLE ${data.tableName}`;
|
||||||
|
changeData.upd.forEach(a=>{
|
||||||
|
updSql += ` MODIFY ${genColumnBasicSql(a)},`
|
||||||
|
})
|
||||||
|
updSql = updSql.substring(0, updSql.length - 1)
|
||||||
|
updSql +=';'
|
||||||
|
}
|
||||||
|
|
||||||
|
if(changeData.del.length > 0){
|
||||||
|
changeData.del.forEach(a=>{
|
||||||
|
delSql += ` ALTER TABLE ${data.tableName} DROP COLUMN ${a.name}; `
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return addSql + updSql + delSql;
|
||||||
|
|
||||||
|
} else if (state.activeName === '2'){ // 修改索引
|
||||||
|
let changeData = filterChangedData(oldData.indexs, state.tableData.indexs.res, 'indexName')
|
||||||
|
// 搜集修改和删除的索引,添加到drop index xx
|
||||||
|
// 收集新增和修改的索引,添加到ADD xx
|
||||||
|
// ALTER TABLE `test1`
|
||||||
|
// DROP INDEX `test1_name_uindex`,
|
||||||
|
// DROP INDEX `test1_column_name4_index`,
|
||||||
|
// ADD UNIQUE INDEX `test1_name_uindex`(`id`) USING BTREE COMMENT 'ASDASD',
|
||||||
|
// ADD INDEX `111`(`column_name4`) USING BTREE COMMENT 'zasf';
|
||||||
|
|
||||||
|
let dropIndexNames: string[] = [];
|
||||||
|
let addIndexs: any[] = [];
|
||||||
|
|
||||||
|
if(changeData.upd.length > 0){
|
||||||
|
changeData.upd.forEach(a=>{
|
||||||
|
dropIndexNames.push(a.indexName)
|
||||||
|
addIndexs.push(a)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if(changeData.del.length > 0){
|
||||||
|
changeData.del.forEach(a=>{
|
||||||
|
dropIndexNames.push(a.indexName)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if(changeData.add.length > 0){
|
||||||
|
changeData.add.forEach(a=>{
|
||||||
|
addIndexs.push(a)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if(dropIndexNames.length > 0 || addIndexs.length > 0){
|
||||||
|
let sql = `ALTER TABLE ${data.tableName} `;
|
||||||
|
if(dropIndexNames.length > 0){
|
||||||
|
dropIndexNames.forEach(a=>{
|
||||||
|
sql += `DROP INDEX ${a},`
|
||||||
|
})
|
||||||
|
sql = sql.substring(0, sql.length - 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
if(addIndexs.length > 0){
|
||||||
|
sql +=','
|
||||||
|
addIndexs.forEach(a=>{
|
||||||
|
sql += ` ADD ${a.unique?'UNIQUE':''} INDEX ${a.indexName}(${a.columnNames.join(',')}) USING ${a.indexType} COMMENT '${a.indexComment}',`;
|
||||||
|
})
|
||||||
|
sql = sql.substring(0, sql.length - 1)
|
||||||
|
}
|
||||||
|
return sql;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const reset = () => {
|
const reset = () => {
|
||||||
formRef.value.resetFields();
|
state.activeName = '1'
|
||||||
|
formRef.value.resetFields()
|
||||||
|
state.tableData.tableName = ''
|
||||||
|
state.tableData.tableComment = ''
|
||||||
state.tableData.fields.res = [
|
state.tableData.fields.res = [
|
||||||
{
|
{
|
||||||
name: '',
|
name: '',
|
||||||
@@ -227,14 +501,76 @@ export default defineComponent({
|
|||||||
remark: '',
|
remark: '',
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
state.tableData.indexs.res = [{
|
||||||
|
indexName: '',
|
||||||
|
columnNames: [],
|
||||||
|
unique: false,
|
||||||
|
indexType: 'BTREE',
|
||||||
|
indexComment: '',
|
||||||
|
},]
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const oldData = {indexs: [] as any[], fields: [] as any[]}
|
||||||
|
watch(()=>props.data, (newValue)=>{
|
||||||
|
const {row, indexs, columns} = newValue;
|
||||||
|
// 回显表名表注释
|
||||||
|
state.tableData.tableName = row.tableName
|
||||||
|
state.tableData.tableComment = row.tableComment
|
||||||
|
// 回显列
|
||||||
|
if(columns && Array.isArray(columns) && columns.length > 0){
|
||||||
|
oldData.fields = [];
|
||||||
|
state.tableData.fields.res = [];
|
||||||
|
// 索引列下拉选
|
||||||
|
state.tableData.indexs.columns = [];
|
||||||
|
columns.forEach(a=>{
|
||||||
|
let typeObj = a.columnType.replace(')','').split('(')
|
||||||
|
let type = typeObj[0];
|
||||||
|
let length = typeObj.length > 1&& typeObj[1] || '';
|
||||||
|
let data = {
|
||||||
|
name: a.columnName,
|
||||||
|
type,
|
||||||
|
value: a.columnDefault || '',
|
||||||
|
length,
|
||||||
|
notNull: a.nullable !== 'YES',
|
||||||
|
pri: a.columnKey === 'PRI',
|
||||||
|
auto_increment: a.extra?.indexOf('auto_increment') > -1,
|
||||||
|
remark: a.columnComment,
|
||||||
|
};
|
||||||
|
state.tableData.fields.res.push(data)
|
||||||
|
oldData.fields.push(JSON.parse(JSON.stringify(data)))
|
||||||
|
// 索引字段下拉选项
|
||||||
|
state.tableData.indexs.columns.push({name: a.columnName, remark: a.columnComment})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
// 回显索引
|
||||||
|
if(indexs && Array.isArray(indexs) && indexs.length > 0){
|
||||||
|
oldData.indexs = [];
|
||||||
|
state.tableData.indexs.res = [];
|
||||||
|
// 索引过滤掉主键
|
||||||
|
indexs.filter(a=>a.indexName!=="PRIMARY").forEach(a=>{
|
||||||
|
let data = {
|
||||||
|
indexName: a.indexName,
|
||||||
|
columnNames: a.columnName?.split(','),
|
||||||
|
unique: a.nonUnique === 0 || false,
|
||||||
|
indexType: a.indexType,
|
||||||
|
indexComment: a.indexComment,
|
||||||
|
}
|
||||||
|
state.tableData.indexs.res.push(data)
|
||||||
|
oldData.indexs.push(JSON.parse(JSON.stringify(data)))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...toRefs(state),
|
...toRefs(state),
|
||||||
formRef,
|
formRef,
|
||||||
cancel,
|
cancel,
|
||||||
reset,
|
reset,
|
||||||
|
addDefaultRows,
|
||||||
addRow,
|
addRow,
|
||||||
deleteRow,
|
deleteRow,
|
||||||
|
addIndex,
|
||||||
|
deleteIndex,
|
||||||
submit,
|
submit,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -17,7 +17,7 @@
|
|||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item prop="host" label="host:" required>
|
<el-form-item prop="host" label="host:" required>
|
||||||
<el-col :span="18">
|
<el-col :span="18">
|
||||||
<el-input :disabled="form.id" v-model.trim="form.host" placeholder="请输入主机ip" auto-complete="off"></el-input>
|
<el-input :disabled="form.id!==undefined" v-model.trim="form.host" placeholder="请输入主机ip" auto-complete="off"></el-input>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col style="text-align: center" :span="1">:</el-col>
|
<el-col style="text-align: center" :span="1">:</el-col>
|
||||||
<el-col :span="5">
|
<el-col :span="5">
|
||||||
@@ -45,7 +45,11 @@
|
|||||||
</el-input>
|
</el-input>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item prop="params" label="连接参数:">
|
<el-form-item prop="params" label="连接参数:">
|
||||||
<el-input v-model="form.params" placeholder="其他连接参数,形如: key1=value1&key2=value2"></el-input>
|
<el-input v-model.trim="form.params" placeholder="其他连接参数,形如: key1=value1&key2=value2">
|
||||||
|
<template v-if="form.id && form.id != 0" #suffix>
|
||||||
|
<el-link target="_blank" href="https://github.com/go-sql-driver/mysql#dsn-data-source-name" :underline="false" type="primary" class="mr5">参数参考</el-link>
|
||||||
|
</template>
|
||||||
|
</el-input>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item prop="database" label="数据库名:" required>
|
<el-form-item prop="database" label="数据库名:" required>
|
||||||
<el-col :span="19">
|
<el-col :span="19">
|
||||||
@@ -53,6 +57,7 @@
|
|||||||
@change="changeDatabase"
|
@change="changeDatabase"
|
||||||
v-model="databaseList"
|
v-model="databaseList"
|
||||||
multiple
|
multiple
|
||||||
|
clearable
|
||||||
collapse-tags
|
collapse-tags
|
||||||
collapse-tags-tooltip
|
collapse-tags-tooltip
|
||||||
filterable
|
filterable
|
||||||
@@ -77,8 +82,8 @@
|
|||||||
<el-col :span="3">
|
<el-col :span="3">
|
||||||
<el-checkbox @change="getSshTunnelMachines" v-model="form.enableSshTunnel" :true-label="1" :false-label="-1"></el-checkbox>
|
<el-checkbox @change="getSshTunnelMachines" v-model="form.enableSshTunnel" :true-label="1" :false-label="-1"></el-checkbox>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="2" v-if="form.enableSshTunnel == 1"> 机器: </el-col>
|
<el-col :span="5" v-if="form.enableSshTunnel == 1"> 机器: </el-col>
|
||||||
<el-col :span="19" v-if="form.enableSshTunnel == 1">
|
<el-col :span="16" v-if="form.enableSshTunnel == 1">
|
||||||
<el-select style="width: 100%" v-model="form.sshTunnelMachineId" placeholder="请选择SSH隧道机器">
|
<el-select style="width: 100%" v-model="form.sshTunnelMachineId" placeholder="请选择SSH隧道机器">
|
||||||
<el-option
|
<el-option
|
||||||
v-for="item in sshTunnelMachineList"
|
v-for="item in sshTunnelMachineList"
|
||||||
|
|||||||
@@ -3,9 +3,7 @@
|
|||||||
<el-card>
|
<el-card>
|
||||||
<el-button v-auth="permissions.saveDb" type="primary" icon="plus" @click="editDb(true)">添加</el-button>
|
<el-button v-auth="permissions.saveDb" type="primary" icon="plus" @click="editDb(true)">添加</el-button>
|
||||||
<el-button v-auth="permissions.saveDb" :disabled="chooseId == null" @click="editDb(false)" type="primary" icon="edit">编辑</el-button>
|
<el-button v-auth="permissions.saveDb" :disabled="chooseId == null" @click="editDb(false)" type="primary" icon="edit">编辑</el-button>
|
||||||
<el-button v-auth="permissions.delDb" :disabled="chooseId == null" @click="deleteDb(chooseId)" type="danger" icon="delete"
|
<el-button v-auth="permissions.delDb" :disabled="chooseId == null" @click="deleteDb(chooseId)" type="danger" icon="delete">删除</el-button>
|
||||||
>删除</el-button
|
|
||||||
>
|
|
||||||
<div style="float: right">
|
<div style="float: right">
|
||||||
<el-select @focus="getTags" v-model="query.tagPath" placeholder="请选择标签" filterable clearable>
|
<el-select @focus="getTags" v-model="query.tagPath" placeholder="请选择标签" filterable clearable>
|
||||||
<el-option v-for="item in tags" :key="item" :label="item" :value="item"> </el-option>
|
<el-option v-for="item in tags" :key="item" :label="item" :value="item"> </el-option>
|
||||||
@@ -79,9 +77,9 @@
|
|||||||
|
|
||||||
<el-dialog width="75%" :title="`${db} 表信息`" :before-close="closeTableInfo" v-model="tableInfoDialog.visible">
|
<el-dialog width="75%" :title="`${db} 表信息`" :before-close="closeTableInfo" v-model="tableInfoDialog.visible">
|
||||||
<el-row class="mb10">
|
<el-row class="mb10">
|
||||||
<el-popover v-model:visible="showDumpInfo" :width="470" placement="right">
|
<el-popover v-model:visible="showDumpInfo" :width="470" placement="right" trigger="click">
|
||||||
<template #reference>
|
<template #reference>
|
||||||
<el-button class="ml5" type="success" size="small" @click="showDumpInfo = !showDumpInfo">导出</el-button>
|
<el-button class="ml5" type="success" size="small" >导出</el-button>
|
||||||
</template>
|
</template>
|
||||||
<el-form-item label="导出内容: ">
|
<el-form-item label="导出内容: ">
|
||||||
<el-radio-group v-model="dumpInfo.type">
|
<el-radio-group v-model="dumpInfo.type">
|
||||||
@@ -105,9 +103,9 @@
|
|||||||
</div>
|
</div>
|
||||||
</el-popover>
|
</el-popover>
|
||||||
|
|
||||||
<el-button type="primary" size="small" @click="tableCreateDialog.visible = true">创建表</el-button>
|
<el-button type="primary" size="small" @click="openEditTable(false)">创建表</el-button>
|
||||||
</el-row>
|
</el-row>
|
||||||
<el-table v-loading="tableInfoDialog.loading" border stripe :data="filterTableInfos" size="small">
|
<el-table v-loading="tableInfoDialog.loading" border stripe :data="filterTableInfos" size="small" max-height="680">
|
||||||
<el-table-column property="tableName" label="表名" min-width="150" show-overflow-tooltip>
|
<el-table-column property="tableName" label="表名" min-width="150" show-overflow-tooltip>
|
||||||
<template #header>
|
<template #header>
|
||||||
<el-input v-model="tableInfoDialog.tableNameSearch" size="small" placeholder="表名: 输入可过滤" clearable />
|
<el-input v-model="tableInfoDialog.tableNameSearch" size="small" placeholder="表名: 输入可过滤" clearable />
|
||||||
@@ -150,7 +148,8 @@
|
|||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
<el-link @click.prevent="showColumns(scope.row)" type="primary">字段</el-link>
|
<el-link @click.prevent="showColumns(scope.row)" type="primary">字段</el-link>
|
||||||
<el-link class="ml5" @click.prevent="showTableIndex(scope.row)" type="success">索引</el-link>
|
<el-link class="ml5" @click.prevent="showTableIndex(scope.row)" type="success">索引</el-link>
|
||||||
<el-link class="ml5" @click.prevent="showCreateDdl(scope.row)" type="info">SQL</el-link>
|
<el-link class="ml5" @click.prevent="openEditTable(scope.row)" type="warning">编辑表</el-link>
|
||||||
|
<el-link class="ml5" @click.prevent="showCreateDdl(scope.row)" type="info">DDL</el-link>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column label="操作" min-width="80">
|
<el-table-column label="操作" min-width="80">
|
||||||
@@ -255,7 +254,7 @@
|
|||||||
v-model:visible="dbEditDialog.visible"
|
v-model:visible="dbEditDialog.visible"
|
||||||
v-model:db="dbEditDialog.data"
|
v-model:db="dbEditDialog.data"
|
||||||
></db-edit>
|
></db-edit>
|
||||||
<create-table :dbId="dbId" :db="db" v-model:visible="tableCreateDialog.visible"></create-table>
|
<create-table :title="tableCreateDialog.title" :active-name="tableCreateDialog.activeName" :dbId="dbId" :db="db" :data="tableCreateDialog.data" v-model:visible="tableCreateDialog.visible"></create-table>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -363,7 +362,17 @@ export default defineComponent({
|
|||||||
title: '新增数据库',
|
title: '新增数据库',
|
||||||
},
|
},
|
||||||
tableCreateDialog: {
|
tableCreateDialog: {
|
||||||
|
title:'创建表',
|
||||||
visible: false,
|
visible: false,
|
||||||
|
columns: [],
|
||||||
|
indexs: [],
|
||||||
|
activeName: '1',
|
||||||
|
data: { // 修改表时,传递修改数据
|
||||||
|
edit: false,
|
||||||
|
row: {},
|
||||||
|
indexs: [],
|
||||||
|
columns: []
|
||||||
|
},
|
||||||
},
|
},
|
||||||
filterDb:{
|
filterDb:{
|
||||||
param:'',
|
param:'',
|
||||||
@@ -657,6 +666,33 @@ export default defineComponent({
|
|||||||
state.filterDb.list = state.filterDb.cache;
|
state.filterDb.list = state.filterDb.cache;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 打开编辑表
|
||||||
|
const openEditTable = async (row: any) => {
|
||||||
|
|
||||||
|
state.tableCreateDialog.visible = true
|
||||||
|
state.tableCreateDialog.activeName = '1'
|
||||||
|
|
||||||
|
if(row === false){
|
||||||
|
state.tableCreateDialog.data = {edit: false, row: {}, indexs: [], columns: []}
|
||||||
|
state.tableCreateDialog.title = '创建表'
|
||||||
|
}
|
||||||
|
|
||||||
|
if (row.tableName) {
|
||||||
|
state.tableCreateDialog.title = '修改表'
|
||||||
|
let indexs = await dbApi.tableIndex.request({
|
||||||
|
id: state.chooseId,
|
||||||
|
db: state.db,
|
||||||
|
tableName: row.tableName,
|
||||||
|
});
|
||||||
|
let columns = await dbApi.columnMetadata.request({
|
||||||
|
id: state.chooseId,
|
||||||
|
db: state.db,
|
||||||
|
tableName: row.tableName,
|
||||||
|
});
|
||||||
|
state.tableCreateDialog.data = {edit: true, row, indexs, columns}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...toRefs(state),
|
...toRefs(state),
|
||||||
@@ -687,6 +723,7 @@ export default defineComponent({
|
|||||||
openSqlExec,
|
openSqlExec,
|
||||||
selectDb,
|
selectDb,
|
||||||
filterSchema,
|
filterSchema,
|
||||||
|
openEditTable,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -146,7 +146,7 @@
|
|||||||
v-loading="queryTab.loading"
|
v-loading="queryTab.loading"
|
||||||
element-loading-text="查询中..."
|
element-loading-text="查询中..."
|
||||||
size="small"
|
size="small"
|
||||||
max-height="250"
|
max-height="800px"
|
||||||
empty-text="tips: select *开头的单表查询或点击表名默认查询的数据,可双击数据在线修改"
|
empty-text="tips: select *开头的单表查询或点击表名默认查询的数据,可双击数据在线修改"
|
||||||
stripe
|
stripe
|
||||||
border
|
border
|
||||||
|
|||||||
@@ -40,7 +40,6 @@ export default defineComponent({
|
|||||||
state.data.colNames = newValue.data.colNames;
|
state.data.colNames = newValue.data.colNames;
|
||||||
});
|
});
|
||||||
const cellClick = (row: any, column: any, cell: any, event: any) => {
|
const cellClick = (row: any, column: any, cell: any, event: any) => {
|
||||||
console.log(cell.children[0].tagName);
|
|
||||||
let isDiv = cell.children[0].tagName === 'DIV';
|
let isDiv = cell.children[0].tagName === 'DIV';
|
||||||
let text = cell.children[0].innerText;
|
let text = cell.children[0].innerText;
|
||||||
let div = cell.children[0];
|
let div = cell.children[0];
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<el-dialog title="待执行SQL" v-model="dialogVisible" :show-close="false" width="600px">
|
<el-dialog title="待执行SQL" v-model="dialogVisible" :show-close="false" width="600px">
|
||||||
|
如需执行多条sql,需要在【数据库管理】配置连接参数:multiStatements=true
|
||||||
<codemirror height="350px" class="codesql" ref="cmEditor" language="sql" v-model="sqlValue" :options="cmOptions" />
|
<codemirror height="350px" class="codesql" ref="cmEditor" language="sql" v-model="sqlValue" :options="cmOptions" />
|
||||||
<el-input ref="remarkInputRef" v-model="remark" placeholder="请输入执行备注" class="mt5" />
|
<el-input ref="remarkInputRef" v-model="remark" placeholder="请输入执行备注" class="mt5" />
|
||||||
<template #footer>
|
<template #footer>
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
export const TYPE_LIST = ['bigint', 'binary', 'blob', 'char', 'datetime', 'decimal', 'double', 'enum', 'float', 'int', 'json', 'longblob', 'longtext', 'mediumblob', 'mediumtext', 'set', 'smallint', 'text', 'time', 'timestamp', 'tinyint', 'varbinary', 'varchar']
|
export const TYPE_LIST = ['bigint', 'binary', 'blob', 'char', 'datetime', 'date', 'decimal', 'double', 'enum', 'float', 'int', 'json', 'longblob', 'longtext', 'mediumblob', 'mediumtext', 'set', 'smallint', 'text', 'time', 'timestamp', 'tinyint', 'varbinary', 'varchar']
|
||||||
|
|
||||||
export const CHARACTER_SET_NAME_LIST = ['armscii8', 'ascii', 'big5', 'binary', 'cp1250', 'cp1251', 'cp1256', 'cp1257', 'cp850', 'cp852', 'cp866', 'cp932', 'dec8', 'eucjpms', 'euckr', 'gb18030', 'gb2312', 'gbk', 'geostd8', 'greek', 'hebrew', 'hp8', 'keybcs2', 'koi8r', 'koi8u', 'latin1', 'latin2', 'latin5', 'latin7', 'macce', 'macroman', 'sjis', 'swe7', 'tis620', 'ucs2', 'ujis', 'utf16', 'utf16le', 'utf32', 'utf8', 'utf8mb4']
|
export const CHARACTER_SET_NAME_LIST = ['armscii8', 'ascii', 'big5', 'binary', 'cp1250', 'cp1251', 'cp1256', 'cp1257', 'cp850', 'cp852', 'cp866', 'cp932', 'dec8', 'eucjpms', 'euckr', 'gb18030', 'gb2312', 'gbk', 'geostd8', 'greek', 'hebrew', 'hp8', 'keybcs2', 'koi8r', 'koi8u', 'latin1', 'latin2', 'latin5', 'latin7', 'macce', 'macroman', 'sjis', 'swe7', 'tis620', 'ucs2', 'ujis', 'utf16', 'utf16le', 'utf32', 'utf8', 'utf8mb4']
|
||||||
|
|
||||||
|
export const COLLATION_SUFFIX_LIST = ['unicode_ci', 'bin', 'croatian_ci', 'czech_ci', 'danish_ci', 'esperanto_ci', 'estonian_ci', 'general_ci', 'german2_ci', 'hungarian_ci', 'icelandic_ci', 'latvian_ci', 'lithuanian_ci', 'persian_ci', 'polish_ci', 'roman_ci', 'romanian_ci', 'sinhala_ci', 'slovak_ci', 'slovenian_ci', 'spanish2_ci', 'spanish_ci', 'swedish_ci', 'turkish_ci', 'unicode_520_ci', 'vietnamese_ci']
|
||||||
|
|||||||
@@ -357,7 +357,8 @@ func GetDbConn(d *entity.Db, db string) (*sql.DB, error) {
|
|||||||
func getDsn(d *entity.Db, db string) string {
|
func getDsn(d *entity.Db, db string) string {
|
||||||
var dsn string
|
var dsn string
|
||||||
if d.Type == entity.DbTypeMysql {
|
if d.Type == entity.DbTypeMysql {
|
||||||
dsn = fmt.Sprintf("%s:%s@%s(%s:%d)/%s?timeout=8s", d.Username, d.Password, d.Network, d.Host, d.Port, db)
|
// 更多参数参考:https://github.com/go-sql-driver/mysql#dsn-data-source-name
|
||||||
|
dsn = fmt.Sprintf("%s:%s@%s(%s:%d)/%s?timeout=8s&multiStatements=true", d.Username, d.Password, d.Network, d.Host, d.Port, db)
|
||||||
if d.Params != "" {
|
if d.Params != "" {
|
||||||
dsn = fmt.Sprintf("%s&%s", dsn, d.Params)
|
dsn = fmt.Sprintf("%s&%s", dsn, d.Params)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,13 +17,13 @@ const (
|
|||||||
WHERE table_schema = (SELECT database())`
|
WHERE table_schema = (SELECT database())`
|
||||||
|
|
||||||
// mysql 索引信息
|
// mysql 索引信息
|
||||||
MYSQL_INDEX_INFO = `SELECT index_name indexName, column_name columnName, index_type indexType,
|
MYSQL_INDEX_INFO = `SELECT index_name indexName, group_concat(column_name) columnName, index_type indexType, non_unique nonUnique,
|
||||||
SEQ_IN_INDEX seqInIndex, INDEX_COMMENT indexComment
|
SEQ_IN_INDEX seqInIndex, INDEX_COMMENT indexComment
|
||||||
FROM information_schema.STATISTICS
|
FROM information_schema.STATISTICS
|
||||||
WHERE table_schema = (SELECT database()) AND table_name = '%s'`
|
WHERE table_schema = (SELECT database()) AND table_name = '%s' GROUP by index_name`
|
||||||
|
|
||||||
// mysql 列信息元数据
|
// mysql 列信息元数据
|
||||||
MYSQL_COLUMN_MA = `SELECT table_name tableName, column_name columnName, column_type columnType,
|
MYSQL_COLUMN_MA = `SELECT table_name tableName, column_name columnName, column_type columnType, column_default columnDefault,
|
||||||
column_comment columnComment, column_key columnKey, extra, is_nullable nullable from information_schema.columns
|
column_comment columnComment, column_key columnKey, extra, is_nullable nullable from information_schema.columns
|
||||||
WHERE table_schema = (SELECT database()) AND table_name in (%s) ORDER BY tableName, ordinal_position`
|
WHERE table_schema = (SELECT database()) AND table_name in (%s) ORDER BY tableName, ordinal_position`
|
||||||
)
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user