using MJTop.Data.SPI; using System; using System.Collections.Generic; using System.Collections.Specialized; using System.Data; using System.Linq; using System.Text.RegularExpressions; using System.Threading.Tasks; namespace MJTop.Data.DatabaseInfo { public class OracleDBInfo : IDBInfo { private DB Db { get; set; } /// /// 数据库工具 /// public Tool Tools { get; private set; } public OracleDBInfo(DB db) { this.Db = db; Refresh(); this.Tools = new Tool(db, this); } public string DBName { get { if (Db.ConnectionStringBuilder is Oracle.ManagedDataAccess.Client.OracleConnectionStringBuilder) { //127.0.0.1:1521/CTMS string source = (Db.ConnectionStringBuilder as Oracle.ManagedDataAccess.Client.OracleConnectionStringBuilder).DataSource; return Regex.Replace(source, @"(.+/)(.+)", "$2"); } else { return (Db.ConnectionStringBuilder as DDTek.Oracle.OracleConnectionStringBuilder).ServiceName; } } } public string User { get { if (Db.ConnectionStringBuilder is Oracle.ManagedDataAccess.Client.OracleConnectionStringBuilder) { return (Db.ConnectionStringBuilder as Oracle.ManagedDataAccess.Client.OracleConnectionStringBuilder).UserID?.ToUpper(); } else { return (Db.ConnectionStringBuilder as DDTek.Oracle.OracleConnectionStringBuilder).UserID?.ToUpper(); } } } public string Version { get; private set; } //Oracle Database 11g Enterprise Edition Release 11.2.0.1.0 - 64bit Production => 11.2 public double VersionNumber { get { var mat = Regex.Match(Version, @"\D*(\d{1,}\.\d{1,})\D*", RegexOptions.Compiled); double.TryParse(mat?.Groups[1]?.Value, out var res); return res; } } public NameValueCollection TableComments { get; private set; } = new NameValueCollection(); public List TableNames { get; private set; } = new List(); public IgCaseDictionary TableInfoDict { get; private set; } public IgCaseDictionary> TableColumnNameDict { get; private set; } public IgCaseDictionary> TableColumnInfoDict { get; private set; } public IgCaseDictionary TableColumnComments { get; private set; } private IgCaseDictionary DictColumnInfo { get; set; } private IgCaseDictionary Dict_Table_Sequence { get; set; } = new IgCaseDictionary(KeyCase.Upper); public NameValueCollection Views { get; private set; } public NameValueCollection Procs { get; private set; } public List DBNames { get { return DBName.TransList(); } } public List Sequences { get; set; } = new List(); public ColumnInfo this[string tableName, string columnName] { get { ColumnInfo colInfo; var strKey = (tableName + "@" + columnName); DictColumnInfo.TryGetValue(strKey, out colInfo); return colInfo; } } public List this[string tableName] { get { List colNames; TableColumnNameDict.TryGetValue(tableName, out colNames); return colNames; } } public string IdentitySeqName(string tableName) { string seqName; if (Dict_Table_Sequence.TryGetValue(tableName, out seqName)) { return seqName; } return string.Empty; } public bool Refresh() { this.DictColumnInfo = new IgCaseDictionary(KeyCase.Upper); this.TableInfoDict = new IgCaseDictionary(KeyCase.Upper); this.TableColumnNameDict = new IgCaseDictionary>(KeyCase.Upper); this.TableColumnInfoDict = new IgCaseDictionary>(KeyCase.Upper); this.TableColumnComments = new IgCaseDictionary(KeyCase.Upper); string sequence_Sql = string.Format("SELECT SEQUENCE_NAME FROM ALL_SEQUENCES WHERE SEQUENCE_OWNER = '{0}' ORDER BY SEQUENCE_NAME", User); string strSql = string.Format("SELECT T.TABLE_NAME as Name, TC.COMMENTS as Value FROM SYS.ALL_ALL_TABLES T, SYS.ALL_TAB_COMMENTS TC WHERE T.IOT_NAME IS NULL AND T.NESTED = 'NO' AND T.SECONDARY = 'N' AND NOT EXISTS ( SELECT 1 FROM SYS.ALL_MVIEWS MV WHERE MV.OWNER = T.OWNER AND MV.MVIEW_NAME = T.TABLE_NAME ) AND TC.OWNER ( + ) = T.OWNER AND TC.TABLE_NAME ( + ) = T.TABLE_NAME AND T.OWNER = '{0}' ORDER BY T.TABLE_NAME ASC", User); string viewSql = string.Format("select view_name,text from ALL_VIEWS WHERE OWNER = '{0}' order by view_name asc", User); //Oracle 11g 推出 LISTAGG 函数,有可能会报:ora-01489 字符串连接的结果过长 string procSql = string.Format("select * from (SELECT name,LISTAGG(text,' ') WITHIN group (order by line asc) text FROM all_source where OWNER = '{0}' group by name ) order by name asc", User); //https://blog.csdn.net/rczrj/article/details/74977010 procSql = string.Format("select * from (SELECT name,xmlagg(xmlparse(content text||' ' wellformed) order by line asc).getclobval() text FROM all_source where OWNER = '{0}' group by name ) order by name asc", User); try { //查询Oracle的所有序列 this.Sequences = Db.ReadList(sequence_Sql); this.TableComments = Db.ReadNameValues(strSql); this.Version = Db.Scalar("select * from v$version where ROWNUM = 1", string.Empty); this.Views = Db.ReadNameValues(viewSql); try { this.Procs = Db.ReadNameValues(procSql); } catch (Exception e) { LogUtils.LogError("查询存储过程报错", Developer.SysDefault, e, this.Version, procSql); this.Procs = new NameValueCollection(); } if (this.TableComments != null && this.TableComments.Count > 0) { this.TableNames = this.TableComments.AllKeys.ToList(); List lstTask = new List(); foreach (string tableName in this.TableNames) { Task task = Task.Run(() => { TableInfo tabInfo = new TableInfo(); tabInfo.TableName = tableName; tabInfo.TabComment = this.TableComments[tableName]; /** 该语句,包含某列是否自增列,查询慢 **/ strSql = @"select a.COLUMN_ID As Colorder,a.COLUMN_NAME As ColumnName,a.DATA_TYPE As TypeName,b.comments As DeText,(Case When a.DATA_TYPE='NUMBER' Then a.DATA_PRECISION When a.DATA_TYPE='NVARCHAR2' Then a.DATA_LENGTH/2 Else a.DATA_LENGTH End )As Length,a.DATA_SCALE As Scale, (Case When (select Count(1) from all_cons_columns aa, all_constraints bb where aa.OWNER = '{0}' and bb.OWNER = '{0}' and aa.constraint_name = bb.constraint_name and bb.constraint_type = 'P' and aa.table_name = '{1}' And aa.column_name=a.COLUMN_NAME)>0 Then 1 Else 0 End ) As IsPK,( case when (select count(1) from all_triggers tri INNER JOIN all_source src on tri.trigger_Name=src.Name where tri.OWNER = '{0}' and src.OWNER = '{0}' and (triggering_Event='INSERT' and table_name='{1}') and regexp_like(text,concat(concat('into\s*?:\s*?new\s*?\.\s*?',a.COLUMN_NAME),'\s+?'),'i'))>0 then 1 else 0 end ) As IsIdentity, Case a.NULLABLE When 'Y' Then 1 Else 0 End As CanNull, a.data_default As DefaultVal from all_tab_columns a Inner Join all_col_comments b On a.TABLE_NAME=b.table_name Where a.OWNER = '{0}' and b.OWNER = '{0}' and b.COLUMN_NAME= a.COLUMN_NAME and a.Table_Name='{1}' order by a.column_ID Asc"; // 忽略 IsIdentity 查询 strSql = @"select a.COLUMN_ID As Colorder,a.COLUMN_NAME As ColumnName,a.DATA_TYPE As TypeName,b.comments As DeText,(Case When a.DATA_TYPE='NUMBER' Then a.DATA_PRECISION When a.DATA_TYPE='NVARCHAR2' Then a.DATA_LENGTH/2 Else a.DATA_LENGTH End )As Length,a.DATA_SCALE As Scale, (Case When (select Count(1) from all_cons_columns aa, all_constraints bb where aa.OWNER = '{0}' and bb.OWNER = '{0}' and aa.constraint_name = bb.constraint_name and bb.constraint_type = 'P' and aa.table_name = '{1}' And aa.column_name=a.COLUMN_NAME)>0 Then 1 Else 0 End ) As IsPK,0 As IsIdentity, Case a.NULLABLE When 'Y' Then 1 Else 0 End As CanNull, a.data_default As DefaultVal from all_tab_columns a Inner Join all_col_comments b On a.TABLE_NAME=b.table_name Where a.OWNER = '{0}' and b.OWNER = '{0}' and b.COLUMN_NAME= a.COLUMN_NAME and a.Table_Name='{1}' order by a.column_ID Asc"; strSql = string.Format(strSql, User, tableName); try { tabInfo.Colnumns = Db.GetDataTable(strSql).ConvertToListObject(); List lstColName = new List(); NameValueCollection nvcColDeText = new NameValueCollection(); foreach (ColumnInfo colInfo in tabInfo.Colnumns) { lstColName.Add(colInfo.ColumnName); nvcColDeText.Add(colInfo.ColumnName, colInfo.DeText); var strKey = (tableName + "@" + colInfo.ColumnName); this.DictColumnInfo.Add(strKey, colInfo); //自增的列,需要查询序列名称 if (colInfo.IsIdentity) { AddColSeq(tableName, colInfo.ColumnName); } if (colInfo.IsPK) { tabInfo.PriKeyColName = colInfo.ColumnName; if (colInfo.IsIdentity) { tabInfo.PriKeyType = PrimaryKeyType.AUTO; } else { tabInfo.PriKeyType = PrimaryKeyType.SET; } } Global.Dict_Oracle_DbType.TryGetValue(colInfo.TypeName, out DbType type); colInfo.DbType = type; } this.TableInfoDict.Add(tableName, tabInfo); this.TableColumnNameDict.Add(tableName, lstColName); this.TableColumnInfoDict.Add(tableName, tabInfo.Colnumns); this.TableColumnComments.Add(tableName, nvcColDeText); } catch (Exception ex) { LogUtils.LogError("DB", Developer.SysDefault, ex, strSql); } }); lstTask.Add(task); if (lstTask.Count(t => t.Status != TaskStatus.RanToCompletion) >= 50) { Task.WaitAny(lstTask.ToArray()); lstTask = lstTask.Where(t => t.Status != TaskStatus.RanToCompletion).ToList(); } } Task.WaitAll(lstTask.ToArray()); } } catch (Exception ex) { LogUtils.LogError("DB", Developer.SysDefault, ex); return false; } return this.TableComments.Count == this.TableInfoDict.Count; } private void AddColSeq(string tableName, string colName) { tableName = (tableName ?? string.Empty); colName = (colName ?? string.Empty); string strSql = string.Empty; if (Sequences != null && Sequences.Count > 0) { foreach (string seqName in Sequences) { strSql = @"select count(1) from all_triggers tri INNER JOIN all_source src on tri.trigger_Name=src.Name where tri.OWNER = '" + User + "' and src.OWNER = '" + User + "' and (triggering_Event='INSERT' and table_name='" + tableName + "') and regexp_like(text,concat(concat('" + seqName + @"\s*?\.\s*?nextval\s+into\s*?:\s*?new\s*?\.\s*?','" + colName + @"'),'\s+?'),'i')"; int res = Db.Single(strSql, 0); if (res > 0) { Dict_Table_Sequence[tableName] = seqName; break; } } } } public Dictionary GetTableStruct_Modify() { string strSql = "select object_name as name ,last_ddl_time as modify_date from all_objects Where OWNER = '" + User + "' and object_Type='TABLE' Order By last_ddl_time Desc"; return Db.ReadDictionary(strSql); } public bool IsExistTable(string tableName) { tableName = (tableName ?? string.Empty); return TableNames.Contains(tableName); } public bool IsExistColumn(string tableName, string columnName) { var strKey = (tableName + "@" + columnName); return DictColumnInfo.ContainsKey(strKey); } public string GetColumnComment(string tableName, string columnName) { Db.CheckTabStuct(tableName, columnName); ColumnInfo colInfo = null; var strKey = (tableName + "@" + columnName); DictColumnInfo.TryGetValue(strKey, out colInfo); return colInfo?.DeText; } public string GetTableComment(string tableName) { Db.CheckTabStuct(tableName); return TableComments[tableName]; } public List GetColumns(string tableName) { Db.CheckTabStuct(tableName); List colInfos = null; TableColumnInfoDict.TryGetValue(tableName, out colInfos); return colInfos; } /// /// 如果表名 或 列名 小写 则加双引号 /// /// 表名或列名 /// private string FormatName(string name) { if (Regex.IsMatch(name, ".*?[a-z].*?", RegexOptions.Compiled)) { return "\"" + name + "\""; } return name; } public bool SetTableComment(string tableName, string comment) { Db.CheckTabStuct(tableName); //tableName = (tableName ?? string.Empty); string upsert_sql = string.Empty; comment = (comment ?? string.Empty).Replace("'", ""); try { upsert_sql = "comment on table \"" + tableName + "\" is '" + comment + "'"; Db.ExecSql(upsert_sql); TableComments[tableName] = comment; var tabInfo = TableInfoDict[tableName]; tabInfo.TabComment = comment; TableInfoDict[tableName] = tabInfo; } catch (Exception ex) { LogUtils.LogError("DB", Developer.SysDefault, ex, upsert_sql); return false; } return true; } public bool SetColumnComment(string tableName, string columnName, string comment) { Db.CheckTabStuct(tableName, columnName); //tableName = (tableName ?? string.Empty); //columnName = (columnName ?? string.Empty); string upsert_sql = string.Empty; comment = (comment ?? string.Empty).Replace("'", ""); try { upsert_sql = "comment on column \"" + tableName + "\".\"" + columnName + "\" is '" + comment + "'"; Db.ExecSql(upsert_sql); List lstColInfo = TableColumnInfoDict[tableName]; NameValueCollection nvcColDesc = new NameValueCollection(); lstColInfo.ForEach(t => { if (t.ColumnName.Equals(columnName,StringComparison.OrdinalIgnoreCase)) { t.DeText = comment; } nvcColDesc.Add(t.ColumnName, t.DeText); }); TableColumnInfoDict.Remove(tableName); TableColumnInfoDict.Add(tableName, lstColInfo); TableColumnComments.Remove(tableName); TableColumnComments.Add(tableName, nvcColDesc); var strKey = (tableName + "@" + columnName); ColumnInfo colInfo = DictColumnInfo[strKey]; colInfo.DeText = comment; DictColumnInfo[strKey] = colInfo; } catch (Exception ex) { LogUtils.LogError("DB", Developer.SysDefault, ex, upsert_sql); return false; } return true; } public bool DropTable(string tableName) { Db.CheckTabStuct(tableName); tableName = (tableName ?? string.Empty); string drop_sql = string.Empty; try { drop_sql = "drop table " + tableName; Db.ExecSql(drop_sql); this.TableComments.Remove(tableName); this.TableNames = TableComments.AllKeys.ToList(); this.TableInfoDict.Remove(tableName); this.TableColumnInfoDict.Remove(tableName); this.TableColumnComments.Remove(tableName); var lstColName = TableColumnNameDict[tableName]; foreach (var colName in lstColName) { var strKey = (tableName + "@" + colName); this.DictColumnInfo.Remove(strKey); } this.TableColumnNameDict.Remove(tableName); } catch (Exception ex) { LogUtils.LogError("DB", Developer.SysDefault, ex, drop_sql); return false; } return true; } public bool DropColumn(string tableName, string columnName) { Db.CheckTabStuct(tableName, columnName); tableName = (tableName ?? string.Empty); columnName = (columnName ?? string.Empty); var strKey = (tableName + "@" + columnName); string drop_sql = "alter table {0} drop column {1}"; try { drop_sql = string.Format(drop_sql, tableName, columnName); Db.ExecSql(drop_sql); this.DictColumnInfo.Remove(strKey); var nvc = TableColumnComments[tableName]; nvc.Remove(columnName); TableColumnNameDict[tableName] = nvc.AllKeys.ToList(); var lstColInfo = TableColumnInfoDict[tableName]; ColumnInfo curColInfo = null; lstColInfo.ForEach(t => { if (t.ColumnName.Equals(columnName)) { curColInfo = t; //tabInfo 对应的 主键类型和主键列 也需要 跟着修改。 if (curColInfo.IsPK) { var tabInfo = TableInfoDict[tableName]; tabInfo.PriKeyType = PrimaryKeyType.UNKNOWN; tabInfo.PriKeyColName = null; TableInfoDict[tableName] = tabInfo; } return; } }); lstColInfo.Remove(curColInfo); TableColumnInfoDict[tableName] = lstColInfo; } catch (Exception ex) { LogUtils.LogError("DB", Developer.SysDefault, ex, drop_sql); return false; } return true; } } }