【问题标题】:Display all tables. Describe-like functionality显示所有表格。描述类功能
【发布时间】:2016-12-06 05:33:00
【问题描述】:

如何显示类似于DESCRIBE myTable 输出的数据库中的所有表。添加功能:

  • 一次所有表
  • 表大小
  • 字符集和排序规则信息
  • 排序功能

注意:DESCRIBE 的输出很简单,一次只输出一个表。

编辑:

Rick James 的反馈很好。我不知所措,需要头脑风暴。

如果有人想添加功能(我的自我回答),例如每个表格底部的缩进行

  • 索引(可能每个索引 1 行显示名称和列名,用逗号分隔
  • 上面那个索引线上的基数
  • 外键约束
  • 您的同龄人可能会发现其他任何有用的东西
  • 在概念上将整个块称为“扩展信息”,以及用于生成它的 Yay 或 Nay 的开关(一个参数)。如果是“N”,则不要生成它。

我会很高兴的。自然,这些信息不会挂在我下面的自我回答中已经显示的列标题下。所以一些视觉像压痕是立即浮现在脑海中的,而不是它完全是表格的一部分。粗略的输出很好。

将以下内容视为可能有帮助的粗略说明:

create schema x99;
use x99;

create table parent
(   -- assume your have only one parent, ok bad example, it's early
    id int auto_increment primary key,
    fullName varchar(100) not null
)ENGINE=InnoDB;

-- drop table child;
create table child
(   id int auto_increment primary key,
    fullName varchar(100) not null,
    myParent int not null,
    CONSTRAINT `mommy_daddy` FOREIGN KEY (myParent) REFERENCES parent(id)
        ON DELETE CASCADE ON UPDATE CASCADE     
)ENGINE=InnoDB;

create table t3
(   id INT AUTO_INCREMENT PRIMARY KEY,
    myD DATE NOT NULL,
    myI INT NOT NULL,
    KEY `t3_001` (myD,myI)
);

create table t4
(   someCode CHAR(4) PRIMARY KEY,
    codeDescr VARCHAR(500) NOT NULL
);

create table t5
(   id INT AUTO_INCREMENT PRIMARY KEY,
    theCode CHAR(4) NOT NULL,
    d1 DATE NOT NULL,
    i1 INT NOT NULL,
    someOther DATETIME NOT NULL,
    FOREIGN KEY `cd_2_t4` (theCode) REFERENCES t4(someCode),
    FOREIGN KEY `cd_2_t3` (d1,i1) REFERENCES t3(myD,myI)
);

-- The below 2 lines are merely to show cardinality which I am sure is
-- read from INFO SCHEMA too
show indexes in child; -- to pick up cardinality (or from INFO SCHEMA)
show indexes in t5; -- ditto
-- So, I am not suggesting to actually call "show indexes"


-- http://dev.mysql.com/doc/refman/5.7/en/key-column-usage-table.html
-- James Goatcher
SELECT CONCAT( table_name, '.', 
column_name, ' -> ', 
referenced_table_name, '.', 
referenced_column_name ) AS list_of_fks 
FROM information_schema.KEY_COLUMN_USAGE 
WHERE REFERENCED_TABLE_SCHEMA = 'x99' 
AND REFERENCED_TABLE_NAME is not null 
ORDER BY TABLE_NAME, COLUMN_NAME; 
+-----------------------------+
| list_of_fks                 |
+-----------------------------+
| child.myParent -> parent.id |
| t5.d1 -> t3.myD             |
| t5.i1 -> t3.myI             |
| t5.theCode -> t4.someCode   |
+-----------------------------+

Despite the output suggested by James Goatcher on that Webpage, 
perhaps what would look better under table t5 as 2 lines:

t5.d1,i1 -> t3.myD,myI              <----- That there would be swell
t5.theCode -> t4.someCode

-- You may make the assumption that all tables are in the same schema
-- If they aren't and it blows up that is fine

drop schema x99;

我想奖励这个赏金。

【问题讨论】:

  • 我正在寻求改进、同行评审和其他建议。
  • 另请注意,DESCRIBE 在涉及复合索引、引擎等方面是不完整的。
  • 目标受众是什么?我对一组SHOW CREATE TABLE 总体上很满意。
  • @RickJames 我正在寻找关于这一点的头脑风暴。寻求表信息(对于所有表或可能在 IN 子句中,所有 这些 表)以获取信息以帮助调试或其他编程工作的人,例如外键约束

标签: mysql


【解决方案1】:

这扩展了给出的答案Here。它添加了字符集和排序规则列,在数据库和表级别也是如此。包括基于两个选项的准系统排序:按字母顺序和按表格大小,计算类似于here。我仍然认为这个概念需要一些同行评审。 DBA here 的 Rolando 正在展示一种方法,Rick James 评论道。这不是一个简单的计算,并且从不包含诸如TEXTBLOBs 之类的非静态数据。因此,请随时改进该计算并分享它。无论如何,如果按“大小”,表格通常会以所需的排序顺序返回。我不保证其准确性与 InnoDB 文件大小有关。

它允许您使用存根来改进排序功能。例如,通过基于主表与支持表和代码表的排序顺序连接的另一个表。

Sessions: 这依赖于 Session 的概念,它只是您调用例程的一个实例。将它们视为以后可以访问的快照。该时间点的数据包含在会话快照中。更改表时可能有用。特别是整理。哦,关于排序规则,外键约束经常由于不正确的排序规则设置而失败,因为开发人员从互联网上剪切和粘贴代码(表级和列级字符集和排序规则不匹配)。这就是为什么我把它扔进这个版本。

例程存在于数据库Reporting101a 中,该数据库包含两个存储过程和一些支持表(几乎都是基于会话的)。大约 5 张桌子。

调用示例:

  1. call Reporting101a.describeTables_v3('myDb',@theOutVar,false,true,'size')
  2. call Reporting101a.describeTables_v3('myDb',@theOutVar,false,true,'alpha')
  3. call Reporting101a.Print_Tables_Like_Describe(4,'size')

注A

参数(第一个存储过程):

  1. 描述所有表的数据库名称。
  2. INTOUT 参数用于保持会话#
  3. boolean:是否要在最后从报表中删除数据
  4. boolean:我们是否应该自动调用 Pretty Printing 存储过程来生成类似describe 的输出。
  5. 排序顺序:“大小”或“阿尔法”。事实上,除“大小”之外的任何内容都会产生“阿尔法”。

参数(Pretty Printing Stored Proc):

  1. 之前保存的快照的会话号。
  2. 按上述排序。

代码记录得很好,没有将它变成 600 行代码而不是 400 行代码。

这些例程在Reporting101a 数据库中是自包含和自引用的。所以从任何地方显式调用它们。

注意A:至于上面的例子:Ex. 1 和 2 类似,只是排序顺序不同。这些是您只需 一个 调用即可使用它的正常方式。表大小始终显示在表名旁边。只有使用 'size' 才能对其进行 降序 排序。第 4 个参数为 True,它在最后自动调用 Pretty Printing Stored Proc。否则,将呈现纯简结果集。 @theOutVar 将被写入,因为它代表创建的会话#。这对于在此后不久手动调用 Pretty Printing 存储过程、将您的一个例程嵌入并使用数据或在几个月后重播结果(冻结快照)很有用。所以,前。 3 是您希望根据先前的调用检索输出数据的情况,已反馈会话#。例如前面提到的示例 1 和 2(其中参数 #4 本来是 False)。或者,如果您只是想重新报告之前的数据快照。

建议的用法是在调用后不删除会话数据。因此,将参数#3 保留为 False,表示不删除。

这些例程不会以任何方式影响您的数据。它只修改Reporting101a 数据库中的数据。

输出:如果例程在命令行之外运行(例如在 MySQL Workbench 中),则表包装列将围绕为输出生成的所有表的整体。它的列标题为“”(空白字符串)。但这仍然很烦人。这可以在下面显示的 Output1 中看到。但是,如果您使用命令行 switches -N -B(跳过列名和批处理模式),例如通过以下调用:

mysql -uYourDBUser -p -N -B -e "call Reporting101a.describeTables_v3('Sample011',@theOutVar,false,true,'size')" > sampleOut.txt

...它将生成一个未包装的输出。更讨喜。写入sampleOut.txt 文件。请参阅下面的输出 2

测试于: 5.55.6.315.7.13

性能:它使用CURSORS 进行漂亮的打印。我通常会嘲笑这个概念。但是考虑到这些是不频繁的调用,并且一两分钟似乎可以接受,我很高兴地发现具有 120 个表的模式的性能不到 10 秒。在我的测试中,Linux 比 Windows 快得多。

两个存储过程(包括顶部的 CREATE SCHEMA):

CREATE SCHEMA IF NOT EXISTS `Reporting101a`;    -- See **Note1**

DROP PROCEDURE IF EXISTS `Reporting101a`.`describeTables_v3`;
DELIMITER $$
CREATE DEFINER=`root`@`localhost` PROCEDURE `Reporting101a`.`describeTables_v3`(
    IN pDBName varchar(100), -- the dbname to report table structures
    OUT theSession int, -- OUT parameter for session# assigned
    IN deleteSessionRows BOOL, -- true for delete rows when done from main reporting table for this session#
    IN callTheSecondStoredProc BOOL, -- TRUE = output is from Pretty output in Second Stored Proc. FALSE= not so pretty output
    IN pOrderBy CHAR(20) -- 'ALPHA' OR 'SIZE'. Alphabetical order, or table size order(desc)
)
BEGIN
    DECLARE thisTable CHAR(100);
    DECLARE beginDT,endDT DATETIME;

    SET beginDT=NOW();
    DROP TEMPORARY TABLE IF EXISTS Reporting101a.tOutput;
    CREATE TEMPORARY TABLE Reporting101a.tOutput
    (   id INT AUTO_INCREMENT PRIMARY KEY,
        tblName VARCHAR(100) NOT NULL,
        ordVal INT NOT NULL,
        cField VARCHAR(100) NOT NULL,
        cType VARCHAR(100) NOT NULL,
        cNull VARCHAR(100) NOT NULL,
        cKey VARCHAR(100) NOT NULL,
        cDefault VARCHAR(100) NULL,
        cExtra VARCHAR(100) NULL,
        cCharSetName VARCHAR(100) NULL,
        cCollName VARCHAR(100) NULL
    );
    DROP TEMPORARY TABLE IF EXISTS Reporting101a.tOutput2;
    CREATE TEMPORARY TABLE Reporting101a.tOutput2
    (   tblName varchar(100) primary key,
        colCount INT NOT NULL,
        cFieldMaxLen INT NOT NULL,
        cTypeMaxLen INT NOT NULL,
        cNullMaxLen INT NOT NULL,
        cKeyMaxLen INT NOT NULL,
        cDefaultMaxLen INT NOT NULL,
        cExtraMaxLen INT NOT NULL,
        cCharSetNameMaxLen INT NOT NULL,
        cCollNameMaxLen INT NOT NULL
    );

    INSERT Reporting101a.tOutput(tblName,ordVal,cField,cType,cNull,cKey,cDefault,cExtra,cCharSetName,cCollName)
    SELECT TABLE_NAME,ORDINAL_POSITION,COLUMN_NAME,COLUMN_TYPE,RPAD(IS_NULLABLE,4,' '), 
    RPAD(COLUMN_KEY,3,' '),RPAD(COLUMN_DEFAULT,7,' '),EXTRA,CHARACTER_SET_NAME,COLLATION_NAME
    FROM INFORMATION_SCHEMA.COLUMNS
    WHERE table_schema = pDBName ORDER BY table_name,ordinal_position; 

    UPDATE Reporting101a.tOutput
    SET cExtra='     '
    WHERE cExtra='';

    UPDATE Reporting101a.tOutput
    SET cField=RPAD(cField,5,' ')
    WHERE LENGTH(cField)<5;

    UPDATE Reporting101a.tOutput
    SET cCharSetName=RPAD(COALESCE(cCharSetName,''),8,' ')
    WHERE LENGTH(COALESCE(cCharSetName,''))<8;

    UPDATE Reporting101a.tOutput
    SET cCollName=RPAD(COALESCE(cCollName,''),9,' ')
    WHERE LENGTH(COALESCE(cCollName,''))<9;

    INSERT Reporting101a.tOutput2(tblName,colCount,cFieldMaxLen,cTypeMaxLen,cNullMaxLen,
    cKeyMaxLen,cDefaultMaxLen,cExtraMaxLen,cCharSetNameMaxLen,cCollNameMaxLen) 
    SELECT tblName,COUNT(*),0,0,0,0,0,0,0,0
    FROM Reporting101a.tOutput 
    GROUP BY tblName;

    UPDATE tOutput2 t2
    JOIN
    (   SELECT tblName,MAX(LENGTH(cField)) AS mField,MAX(LENGTH(cType)) AS mType,MAX(LENGTH(cNull)) AS mNull,
        IFNULL(MAX(LENGTH(cKey)),0) AS mKey,IFNULL(MAX(LENGTH(cDefault)),0) AS mDefault,IFNULL(MAX(LENGTH(cExtra)),0) AS mExtra,
        IFNULL(MAX(LENGTH(cCharSetName)),0) AS mCharSetName,IFNULL(MAX(LENGTH(cCollName)),0) AS mCollName
        FROM Reporting101a.tOutput
        GROUP BY tblName
    ) x
    ON x.tblName=t2.tblName
    SET t2.cFieldMaxLen=x.mField,t2.cTypeMaxLen=x.mType,cNullMaxLen=x.mNull,cKeyMaxLen=x.mKey,
    cDefaultMaxLen=x.mDefault,cExtraMaxLen=x.mExtra,cCharSetNameMaxLen=x.mCharSetName,cCollNameMaxLen=x.mCollName;

    CREATE TABLE IF NOT EXISTS Reporting101a.reportDataSessions
    (   -- For the purpose of safe session auto_inc usage, timings, and rowcount
        -- Please don't delete unless you want the sessions to experience aberrant behavior.
        -- That is, the inability to report on prior sessions run. Which is no big deal.
        sessionId INT AUTO_INCREMENT PRIMARY KEY,
        dbName VARCHAR(100) NOT NULL,
        -- character_set_name VARCHAR(100) NULL,
        -- collation_name VARCHAR(100) NULL,
        creationDT DATETIME NOT NULL,
        partA_BeginDT DATETIME NULL,
        partA_EndDT DATETIME NULL,   -- See the following for fractional seconds:
        partB_BeginDT DATETIME NULL, -- http://dev.mysql.com/doc/refman/5.7/en/fractional-seconds.html
        partB_EndDT DATETIME NULL,
        rowCount INT NULL
    );

    CREATE TABLE IF NOT EXISTS Reporting101a.reportDataColumns
    (   sessionId INT NOT NULL,
        tblName VARCHAR(100) NOT NULL,  -- Tablename
        ordVal INT NOT NULL,    -- the "position number" of the Column
        cField VARCHAR(100) NOT NULL,   -- The Column
        cType VARCHAR(100) NOT NULL,    -- Datatype
        cNull VARCHAR(100) NOT NULL,    -- Nullability
        cKey VARCHAR(100) NOT NULL, -- Key info
        cDefault VARCHAR(100) NULL, -- Default value
        cExtra VARCHAR(100) NULL,   -- Extra output
        cCharSetName VARCHAR(100) NULL, -- Default value
        cCollName VARCHAR(100) NULL,    -- Extra output
        colCount INT NOT NULL,  -- the columns here and below are de-normalize data
        cFieldMaxLen INT NOT NULL,
        cTypeMaxLen INT NOT NULL,
        cNullMaxLen INT NOT NULL,
        cKeyMaxLen INT NOT NULL,
        cDefaultMaxLen INT NOT NULL,
        cExtraMaxLen INT NOT NULL,
        cCharSetNameMaxLen INT NOT NULL,
        cCollNameMaxLen INT NOT NULL
    );

    CREATE TABLE IF NOT EXISTS Reporting101a.reportDataTables
    (   sessionId INT NOT NULL,
        tblName VARCHAR(100) NOT NULL,  -- tablename
        character_set_name VARCHAR(100) NULL, -- table-level default char set
        collation_name VARCHAR(100) NULL, -- table-level default collation
        rowcount BIGINT NULL, -- rowcount (subject to system refresh, ditto, next column)
        tblSizeMB DECIMAL(14,2) NULL -- in MB
    );

    CREATE TABLE IF NOT EXISTS Reporting101a.reportDataDatabases
    (   sessionId INT NOT NULL,
        dbName VARCHAR(100) NOT NULL,   -- Tablename
        character_set_name VARCHAR(100) NULL, -- db-level default char set
        collation_name VARCHAR(100) NULL -- db-level default collation
    );

    -- For lack of a better notion, we are calling calls "sessions". The programmer calls the
    -- First Stored Proc, and we call that a session after we get a unique next incrementing number.
    -- That number is the session #. House all output with that as a column value. This allows us to 
    -- move between stored procs, have safe output, have historical snapshots, and retain the data 
    -- via a session # for later use, whatever use.
    INSERT Reporting101a.reportDataSessions(dbName,creationDT) VALUES (pDBName,now());
    SET @mySession=LAST_INSERT_ID(); -- there it is, our session # (read the above paragraph)

    INSERT Reporting101a.reportDataColumns(sessionId,tblName,ordVal,cField,cType,cNull,cKey,cDefault,cExtra,cCharSetName,cCollName,
    colCount,cFieldMaxLen,cTypeMaxLen,cNullMaxLen,cKeyMaxLen,cDefaultMaxLen,cExtraMaxLen,cCharSetNameMaxLen,cCollNameMaxLen)    
    SELECT @mySession,t1.tblName,t1.ordVal,t1.cField,t1.cType,t1.cNull,t1.cKey,t1.cDefault,t1.cExtra,t1.cCharSetName,t1.cCollName,
    t2.colCount,t2.cFieldMaxLen,t2.cTypeMaxLen,t2.cNullMaxLen,t2.cKeyMaxLen,t2.cDefaultMaxLen,t2.cExtraMaxLen,t2.cCharSetNameMaxLen,t2.cCollNameMaxLen
    FROM Reporting101a.tOutput t1
    JOIN Reporting101a.tOutput2 t2
    ON t2.tblName=t1.tblName
    ORDER BY t1.tblName,t1.id;

    INSERT Reporting101a.reportDataTables(sessionId,tblName,character_set_name,collation_name,rowcount,tblSizeMB)
    SELECT DISTINCT @mySession,tblName,NULL,NULL,NULL,NULL
    FROM Reporting101a.reportDataColumns
    WHERE sessionId=@mySession;

    -- http://dev.mysql.com/doc/refman/5.7/en/collation-character-set-applicability-table.html
    -- TLDR; A collation can map to a character set
    UPDATE Reporting101a.reportDataTables rdt
    JOIN INFORMATION_SCHEMA.`TABLES` ist
    ON ist.TABLE_SCHEMA=pDBName AND ist.TABLE_NAME=rdt.tblName
    JOIN INFORMATION_SCHEMA.`COLLATION_CHARACTER_SET_APPLICABILITY` isccsa
    ON isccsa.COLLATION_NAME=ist.TABLE_COLLATION
    SET rdt.character_set_name=isccsa.CHARACTER_SET_NAME,rdt.collation_name=isccsa.COLLATION_NAME,
    rdt.rowcount=ist.TABLE_ROWS,rdt.tblSizeMB=round(((ist.data_length+ist.index_length) / 1048576), 2)
    WHERE rdt.sessionId=@mySession;

    INSERT Reporting101a.reportDataDatabases(sessionId,dbName,character_set_name,collation_name)
    SELECT @mySession,pDBName,DEFAULT_CHARACTER_SET_NAME,DEFAULT_COLLATION_NAME
    FROM INFORMATION_SCHEMA.SCHEMATA
    WHERE schema_name = pDBName;

    DROP TEMPORARY TABLE Reporting101a.tOutput;
    DROP TEMPORARY TABLE Reporting101a.tOutput2;
    SET theSession=@mySession; -- the OUT var that came in as a parameter
    SET endDT=NOW();
    UPDATE Reporting101a.reportDataSessions 
    SET partA_BeginDT=beginDT,partA_EndDT=endDT 
    WHERE sessionId=@mySession;
    -- ***************************************************************************
    -- ***************************************************************************
    -- Label "Some_Sort_of_Output":
    IF callTheSecondStoredProc=TRUE THEN
        -- The caller says to call the second stored proc (for Pretty Printing)
        -- This will generate output similar to `DESCRIBE myTable`
        -- But remember, it will do it  for EVERY table in referenced database
        CALL Reporting101a.`Print_Tables_Like_Describe`(@mySession,pOrderBy);
        -- The above call just gave you output.
    ELSE
        -- The caller chose to not auto call the Pretty Printing second stored procedure.
        -- Note, the caller can easily call it right after using the OUT parameter.
        -- So our output will be a resultset of out reportDataColumns table for this session #
        IF pOrderBy!='size' THEN
            -- Order by Alpha for any parameter except 'size'
            SELECT * 
            FROM Reporting101a.reportDataColumns 
            WHERE sessionId=@mySession
            ORDER BY tblName,ordVal;
        ELSE
            -- Order By size DESC
            SELECT rdc.* 
            FROM Reporting101a.reportDataTables rdt
            JOIN Reporting101a.reportDataColumns rdc 
            ON rdc.tblName=rdt.tblName and rdc.sessionId=rdt.sessionId
            WHERE rdt.sessionId=@mySession
            ORDER BY rdt.tblSizeMB DESC,rdc.tblName,rdc.ordVal;
        END IF;
    END IF;
    -- ***************************************************************************
    -- ***************************************************************************

    IF deleteSessionRows=TRUE THEN
        -- The caller says output rows are NOT needed at this point. Delete them.
        -- Note, if this boolean comes in TRUE, you can't call Pretty Printing
        -- second stored procedure with the session # because the data is gone.
        --
        -- Regardless, you are getting something back from "Some_Sort_of_Output" above.
        DELETE FROM Reporting101a.reportDataColumns
        WHERE sessionId=@mySession;

        DELETE FROM Reporting101a.reportDataTables
        WHERE sessionId=@mySession;

        -- Do not delete the row from the Sessions table for now (you can, I'm not atm)
    END IF;
END$$
DELIMITER ;

-- *****************************************************************
-- *****************************************************************
-- *****************************************************************

DROP PROCEDURE IF EXISTS `Reporting101a`.`Print_Tables_Like_Describe`;
DELIMITER $$
CREATE DEFINER=`root`@`localhost` PROCEDURE `Reporting101a`.`Print_Tables_Like_Describe`(
    IN pSessionId INT,
    IN pOrderBy CHAR(20) -- 'size' or 'alpha' (see CURSORs below). Size means Tablesize (see ____).
)
BEGIN
    -- Please note: CURSOR stuff must come last in DECLAREs, else "Error 1337: Variable or condition decl aft curs" 
    DECLARE beginDT,EndDT DATETIME; 
    DECLARE done INT DEFAULT FALSE;
    DECLARE curTable VARCHAR(100) DEFAULT '';
    DECLARE bFirst BOOL DEFAULT TRUE;
    DECLARE lv_dbName,CharSetName,CollationName,someClueToCaller VARCHAR(100);
    DECLARE lv_rowCount,lineCount INT;
    DECLARE theSize DECIMAL(14,2);
    DECLARE lv_sessionDT DATETIME;
    DECLARE lv_tblName,lv_cField,lv_cType,lv_cNull,lv_cKey,lv_cDefault,lv_cExtra,lv_cCharSetName,lv_cCollName VARCHAR(100);
    DECLARE lv_ordVal,lv_colCount,lv_cFieldMaxLen,lv_cTypeMaxLen,lv_cNullMaxLen,lv_cKeyMaxLen,
    lv_cDefaultMaxLen,lv_cExtraMaxLen,lv_cCharSetNameMaxLen,lv_cCollNameMaxLen INT;
    -- -------------------------------------------------------------------------------------------------------------------
    -- The below cursor is in Alphabetical Ascending order
    DECLARE curAlpha CURSOR FOR SELECT tblName,ordVal,cField,cType,cNull,cKey,cDefault,cExtra,cCharSetName,cCollName,
    colCount,cFieldMaxLen,cTypeMaxLen,cNullMaxLen,cKeyMaxLen,cDefaultMaxLen,cExtraMaxLen,cCharSetNameMaxLen,cCollNameMaxLen
    FROM Reporting101a.reportDataColumns
    WHERE rdt.sessionId=pSessionId
    ORDER BY tblName,ordVal; -- ascending order by tablename then the ordinal position of each column (1..n)

    -- The below cursor is in Tablesize Descending order, followed by tablename + ordinal position ascending
    DECLARE curSize CURSOR FOR SELECT rdc.tblName,rdc.ordVal,rdc.cField,rdc.cType,rdc.cNull,rdc.cKey,rdc.cDefault,
    rdc.cExtra,rdc.cCharSetName,rdc.cCollName,rdc.colCount,rdc.cFieldMaxLen,rdc.cTypeMaxLen,rdc.cNullMaxLen,
    rdc.cKeyMaxLen,rdc.cDefaultMaxLen,rdc.cExtraMaxLen,rdc.cCharSetNameMaxLen,rdc.cCollNameMaxLen
    FROM Reporting101a.reportDataTables rdt
    JOIN Reporting101a.reportDataColumns rdc
    ON rdc.tblName=rdt.tblName and rdc.sessionId=rdt.sessionId
    WHERE rdt.sessionId=pSessionId
    ORDER BY rdt.tblSizeMB DESC,rdc.tblName,rdc.ordVal; -- tablesize desc, then tablename + ordinal position ascending

    -- What is the Ordinal Position? Simply 1..n as saved in the db 
    -- (see ORDINAL_POSITION in INFORMATION_SCHEMA.COLUMNS)
    --
    -- If it is terribly bothersome, then look into 
    -- "ALTER TABLE" and use FIRST or AFTER   .. :)

    DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;

    -- Please note in the above, CURSOR stuff MUST come LAST else "Error 1337: Variable or condition decl aft curs" 
    -- -------------------------------------------------------------------------------------------------------------------
    SET beginDT=NOW();
    SET lineCount=0;
    CREATE TABLE IF NOT EXISTS Reporting101a.reportOutput
    (   lineNum INT AUTO_INCREMENT PRIMARY KEY,
        sessionId INT NOT NULL,
        lineOut varchar(200) NOT NULL
    );

    DELETE FROM Reporting101a.reportOutput WHERE sessionId=pSessionId; -- cleans up a prior run with this session#

    IF pOrderBy!='size' THEN
        OPEN curAlpha; -- we are in using the Alphabetical Cursor (includes typos from caller for the sort column)
    ELSE
        OPEN curSize; -- we are in using the Tablesize Cursor
    END IF;

    -- **Place004** (Top-most output, Session #, then get top-level database info, there is not much of it)
    -- Here is the importance of it though: it documents the state of things at that point in time
    -- And it allows for reporting later by using that Session # (so that is why a Session # should hang out and remain)
    -- So, a snapshot. You don't even need to print and use the output now. You can have it for later. To compare.
    SELECT creationDT INTO lv_sessionDT FROM Reporting101a.reportDataSessions WHERE sessionId=pSessionId;

    SET someClueToCaller='Typo from caller, using Alphabetical';
    IF pOrderBy='size' THEN
        SET someClueToCaller='table size DESC';
    END IF;
    IF pOrderBy='alpha' THEN
        SET someClueToCaller='tablename alphabetical';
    END IF;
    INSERT Reporting101a.reportOutput(sessionId,lineOut)
    SELECT pSessionId,CONCAT('Session: ', pSessionId, ', Date:',lv_sessionDT,' , SortOrder: ',someClueToCaller);

    INSERT Reporting101a.reportOutput(sessionId,lineOut) VALUES (pSessionId,''); -- blank line

    SELECT dbName,character_set_name,collation_name INTO lv_dbName,CharSetName,CollationName
    FROM Reporting101a.reportDataDatabases
    WHERE sessionId=pSessionId;

    INSERT Reporting101a.reportOutput(sessionId,lineOut)
    SELECT pSessionId,CONCAT('Database: ', lv_dbname, ' (CharSet=', COALESCE(CharSetName,''), ', Collation=', COALESCE(CollationName,''), ')');

    INSERT Reporting101a.reportOutput(sessionId,lineOut) VALUES (pSessionId,''); -- blank line
    -- end **Place004** ----------------------------------------------------------------------


    read_loop: LOOP
        IF pOrderBy!='size' THEN
            FETCH curAlpha INTO lv_tblName,lv_ordVal,lv_cField,lv_cType,lv_cNull,lv_cKey,lv_cDefault,lv_cExtra,lv_cCharSetName,
            lv_cCollName,lv_colCount,lv_cFieldMaxLen,lv_cTypeMaxLen,lv_cNullMaxLen,lv_cKeyMaxLen,lv_cDefaultMaxLen,
            lv_cExtraMaxLen,lv_cCharSetNameMaxLen,lv_cCollNameMaxLen ;
        ELSE
            FETCH curSize INTO lv_tblName,lv_ordVal,lv_cField,lv_cType,lv_cNull,lv_cKey,lv_cDefault,lv_cExtra,lv_cCharSetName,
            lv_cCollName,lv_colCount,lv_cFieldMaxLen,lv_cTypeMaxLen,lv_cNullMaxLen,lv_cKeyMaxLen,lv_cDefaultMaxLen,
            lv_cExtraMaxLen,lv_cCharSetNameMaxLen,lv_cCollNameMaxLen ;
        END IF;
        IF done THEN
            LEAVE read_loop;
        END IF;
        IF lv_tblName<>curTable THEN -- **Place006**, "the IF"
            -- We stumbled into a "This is a New table condition (different table)
            -- So we need to create the New Chunk Header Info (also get table-level info like rowcount etc ie **Place7** below)
            -- (as opposed to just displaying the next column info on a table we were already working with)
            IF bFirst=FALSE THEN
                INSERT Reporting101a.reportOutput(sessionId,lineOut)
                SELECT pSessionId,''; -- Insert a blank line between tables (but not the first time)
            ELSE
                SET bFirst=FALSE;
            END IF;

            -- **Place007** (get top-level table info, there is not much of it, just, like, rowcount, charset, collation)
            SELECT rowcount,character_set_name,collation_name,tblSizeMB INTO lv_rowCount,CharSetName,CollationName,theSize
            FROM Reporting101a.reportDataTables
            WHERE sessionId=pSessionId AND tblName=lv_tblName;

            INSERT Reporting101a.reportOutput(sessionId,lineOut)
            SELECT pSessionId,CONCAT(lv_tblName,' (rowcount=',ifnull(lv_rowCount,0),') (Size=',theSize,'MB) (CharSet=',COALESCE(CharSetName,''), ', Collation=',COALESCE(CollationName,''),')');
            -- end **Place007** ----------------------------------------------------------------------


            INSERT Reporting101a.reportOutput(sessionId,lineOut)
            SELECT pSessionId,CONCAT('+-', 
                REPEAT('-',GREATEST(5,lv_cFieldMaxLen)),  '-+-',
                REPEAT('-',GREATEST(4,lv_cTypeMaxLen)), '-+-',
                REPEAT('-',GREATEST(4,lv_cNullMaxLen)), '-+-',
                REPEAT('-',GREATEST(3,lv_cKeyMaxLen)), '-+-',
                REPEAT('-',GREATEST(7,lv_cDefaultMaxLen)), '-+-',
                REPEAT('-',GREATEST(5,lv_cExtraMaxLen)), '-+-',
                REPEAT('-',GREATEST(8,lv_cCharSetNameMaxLen)),'-+-',
                REPEAT('-',GREATEST(5,lv_cCollNameMaxLen)), '-+');

            SET @dashLineNumRow=LAST_INSERT_ID(); -- **Place008**: Save this row id so we can use it again in one sec

            INSERT Reporting101a.reportOutput(sessionId,lineOut)
            SELECT pSessionId,CONCAT('| ', 
                'Field',
                REPEAT(' ',GREATEST(0,lv_cFieldMaxLen-5)),  ' | ',
                'Type',
                REPEAT(' ',GREATEST(0,lv_cTypeMaxLen-4)),   ' | ',
                'Null',
                REPEAT(' ',GREATEST(0,lv_cNullMaxLen-4)),   ' | ',
                'Key',
                REPEAT(' ',GREATEST(0,lv_cKeyMaxLen-3)),    ' | ',
                'Default',
                REPEAT(' ',GREATEST(0,lv_cDefaultMaxLen-7)),    ' | ',
                'Extra',
                REPEAT(' ',GREATEST(0,lv_cExtraMaxLen-5)),  ' | ',
                'Char Set',
                REPEAT(' ',GREATEST(0,lv_cCharSetNameMaxLen-8)),    ' | ',
                'Collation',
                REPEAT(' ',GREATEST(0,lv_cCollNameMaxLen-9)),   ' |');

            INSERT Reporting101a.reportOutput(sessionId,lineOut)
            SELECT pSessionId,lineOut FROM Reporting101a.reportOutput
            WHERE lineNum=@dashLineNumRow; -- related to **Place008** above (just repeat it to close-off header)

            SET curTable=lv_tblName; -- set the variable which is our flag for Next/New/Different table (related: **Place006** above)
        END IF;

        -- The below is the generic insert for a column's info
        INSERT Reporting101a.reportOutput(sessionId,lineOut)
        SELECT pSessionId,
            CONCAT('| ', 
            lv_cField,
            COALESCE(REPEAT(' ',GREATEST(0,lv_cFieldMaxLen-LENGTH(lv_cField))),''),' | ',
            COALESCE(lv_cType,''),
            COALESCE(REPEAT(' ',GREATEST(0,lv_cTypeMaxLen-LENGTH(lv_cType))),''),' | ',
            COALESCE(lv_cNull,''),
            COALESCE(REPEAT(' ',GREATEST(0,lv_cNullMaxLen-LENGTH(lv_cNull))),''),' | ',
            COALESCE(lv_cKey,'   '),
            COALESCE(REPEAT(' ',GREATEST(0,lv_cKeyMaxLen-LENGTH(lv_cKey))),''),' | ',
            COALESCE(lv_cDefault,'       '),
            COALESCE(REPEAT(' ',GREATEST(0,lv_cDefaultMaxLen-LENGTH(lv_cDefault))),''),' | ',
            COALESCE(lv_cExtra,'     '),
            COALESCE(REPEAT(' ',GREATEST(0,lv_cExtraMaxLen-LENGTH(lv_cExtra))),''),' | ',
            lv_cCharSetName, 
            REPEAT(' ',GREATEST(0,lv_cCharSetNameMaxLen-LENGTH(lv_cCharSetName))),' | ',
            lv_cCollName,
            REPEAT(' ',GREATEST(0,lv_cCollNameMaxLen-LENGTH(lv_cCollName))),' |');
        SET lineCount=lineCount+1; -- increment only for column rows not separator rows
        INSERT Reporting101a.reportOutput(sessionId,lineOut)
        SELECT pSessionId,lineOut
        FROM Reporting101a.reportOutput
        WHERE lineNum=@dashLineNumRow;
    END LOOP;
    IF pOrderBy!='size' THEN
        CLOSE curAlpha;
    ELSE 
        CLOSE curSize;
    END IF;

    SET endDT=NOW();

    UPDATE Reporting101a.reportDataSessions 
    SET partB_BeginDT=beginDT,partB_EndDT=endDT,rowCount=lineCount
    WHERE sessionId=pSessionId;

    SELECT lineOut AS '' from Reporting101a.reportOutput WHERE sessionId=pSessionId ORDER BY lineNum;
    -- Note: The whole outer box wrapper is suppressed (which is cool) if we perform a
    --
    -- OSPrompt> mysql -N -B -u UserName -p -e "call Reporting101a.describeTables_v3('stackoverflow',@theOutVar,false,true,'size')"
    --
    -- That above -N -B suppresses column info (-N), ... (-B) keeps the output left aligned and is Batch mode
    -- I understand (-N), but without (-B) the alignment goes right-aligned
    -- Regardless, it allows us to perform what would appear to 
    -- be merely PRINT statements, if you will. No outer box wrapper in output.
END$$
DELIMITER ;

Output1(显示包装/包含外部表)。两个黄色荧光笔是表格名称。

Output2(使用-N -B 开关的命令行)删除了外包装表。请参阅上面 输出 部分中的示例以了解用法。 Effective 允许在 MySQL 中使用类似 PRINT 的命令。

【讨论】:

  • 一切都发生在一瞬间! :p
  • 如果您有很多表,查询可能会很慢。他们必须有效地打开每张桌子。 (5.8 的内置数据字典可以解决这个问题。)
  • 请记住,InnoDB 表的行数是近似值。
  • @RickJames 发布赏金的用户不能将其奖励给自己。查看常见问题解答中的Bounty post
  • @RickJames 我为什么要把它奖励给自己?
猜你喜欢
  • 1970-01-01
  • 2023-03-24
  • 2013-09-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-03-09
相关资源
最近更新 更多