web-dev-qa-db-ja.com

MS SQLの結果を垂直に表示する方法は?

以下は、sqlcmdでクエリを実行したときに現在取得しているものです。

1> SELECT 1,2,3,4
2> GO

----------- ----------- ----------- -----------
          1           2           3           4

(1 rows affected)

より長い結果の場合、この出力フォーマットは人間が読める形式ではありませんが、ターミナルでラップされるためです。

結果を垂直に表示するにはどうすればよいですか( \G in mysql )?または人間が読める別の方法で?

8
kenorb

SELECT F1, F2, F2を使用した場合の期待される結果は、SELECT文の各フィールドに対して1つの列を取得することです。

SELECT '1' + CHAR(13) + '2' + CHAR(13) + '3' + CHAR(13)

------
1
2
3


(1 row(s) affected)
5
McNets

UNIONを使用できます。

select 1 union select 2 union select 3 union select 4

結果:

-----------
1
2
3
4

(4 rows affected)
5
Gary

結果ウィンドウに結果を垂直に表示する への純粋なT-SQLアプローチを見つけました。これには、このリンクからコピーしているカスタムストアドプロシージャ(sp_SHOWDOWN)が含まれます。あなたはそれで遊んで、それが役立つかどうかを確認することができます。

著者はいくつかの制限を示しています:

  • クエリは、結合がいくつでも必要なだけ複雑になる可能性がありますが、一時テーブルのため、列名は一意である必要があります。
  • 多数のレコードを返そうとすると、非常に遅くなります。 10以下のレコードを返すクエリに最適です。
  • テキストおよび画像フィールドのDATALENGTHを表示します

ここに簡単な説明があります(投稿から引用):

この手順を使用すると、画面全体ではなく垂直方向(下)に結果を表示できます。 50列と2、3レコードしかないクエリを処理する場合に最適です。

set ANSI_NULLS ON
set QUOTED_IDENTIFIER ON
GO
/********************************************************************************************************
** NAME: sp_ShowDown
**
** DESC: Display (SHOWs) the results of a SELECT vertically (DOWN) instead of horizontally  
**       The query can be as complex as necessary with as many joins however the column names
**       must be unique because of the temp table.  Note image and text fields display only 
**       their size (DATALENGTH)
**
** PARM: @Help = 1 will display syntax and instructions
**
** RETR: the resultset of the records.  Notes displaying a lot of records will take a LONG time.  Generally
**       this should be used for recordsets of no more than 10.  
**
** AUTH: D. Simmons
**
** SYNTAX  sp_showdown 1 -- displays full syntax on how to run
**
** MOD DATE:
** 05.22.07 - DTS Prevent casting of image & text to varchar
** 05.20.07 - DTS original version
*********************************************************************************************************/
ALTER PROCEDURE [dbo].[sp_ShowDown] (
    @help   BIT = NULL
)
AS

SET NOCOUNT ON

    -- ------------------------------------------------------------------------
    -- DECLARATION AND TABLE CREATION
    -- ------------------------------------------------------------------------

    DECLARE 
        @Column     VARCHAR(60),        -- the fieldname
        @CurrOrdPos INT,                -- the order of the column in the table
        @SQL        VARCHAR(1000),      -- dynamic select statement
        @SQ         CHAR(1),            -- single quote
        @MaxTable   VARCHAR(1000),      -- holds the tempwide2 name - the true one stored in tempdb
        @RecordID   INT,                -- each record's number to aid in sorting when more than one record is return
        @DataType   VARCHAR(25),        -- the datatype of the field
        @FieldName  VARCHAR(200)            -- will hold column's name with brackets ready for the SELECT               

    IF OBJECT_ID('tempdb..#tempdown') IS NOT NULL DROP TABLE #tempdown

    CREATE TABLE #tempdown (
        Rec         INT,                -- short column names on purpose so it doesn't take up much 
        Ord         INT,                -- space in final result
        ColumnName  VARCHAR(60),        -- the columnname 
        Data        VARCHAR(7500)       -- the data for the column
    )

    -- ------------------------------------------------------------------------
    -- INITIALIZE
    -- ------------------------------------------------------------------------

    SET @RecordID = 0

    -- CONSTANTS
    SET @SQ = CHAR(39)      -- single quote


    -- ------------------------------------------------------------------------
    -- LOGIC
    -- ------------------------------------------------------------------------ 

    -- print the syntax and usage instructions to the result window
    IF @Help = 1 BEGIN
        PRINT 'Keep in mind that with temp tables the column names must be unique!'
        PRINT ' '
        PRINT 'Example of syntax: '
        PRINT ' '
        PRINT 'IF OBJECT_ID(''tempdb..#tempwide'') IS NOT NULL DROP TABLE #tempwide  -- ADD TO TOP OF YOUR SELECT'
        PRINT ' '
        PRINT 'SELECT TOP 1 * '
        PRINT 'INTO #tempwide           -- ADD THIS TO YOUR QUERY'
        PRINT 'FROM authors a'
        PRINT ' ' 
        PRINT 'EXEC _SHOWDOWN               -- ADD AS THE LAST LINE'
        PRINT '  '                         
        PRINT 'COPY THESE LINES and place where instructed'
        PRINT 'IF OBJECT_ID(''tempdb..#tempwide'') IS NOT NULL DROP TABLE #tempwide'
        PRINT 'INTO #tempwide'
        PRINT 'EXEC sp_SHOWDOWN'

        RETURN
    END

    -- Create a new 'wide' table so we can add a RecordID (DIDROCER) which allows muliple records and their fields 
    -- to be grouped together.  DIDROCER is RecordID backwards.  Needed a field name that will have an unlikely
    -- chance of ever being in a real table since it will be excluded from the results displayed vertically.
    SELECT  0 'DIDROCER', *
    INTO    #tempwide2
    FROM    #tempwide

    -- increment the record id for the table
    UPDATE  #tempwide2 SET  @RecordID = DIDROCER = @RecordID + 1

    -- get name of tempwide2 table (the true name in tempdb)
    SET @MaxTable = (   SELECT  MAX(TABLE_NAME) 
                        FROM    tempdb.INFORMATION_SCHEMA.TABLES
                        WHERE   Table_Name LIKE '%#tempwide2%'
                    )

    -- get the min ord position for the first column for my temp table.  Eliminates need for cursor
    SET @CurrOrdPos = ( SELECT  MIN(Ordinal_Position) 
                        FROM    tempdb.INFORMATION_SCHEMA.COLUMNS 
                        WHERE   Table_Name LIKE '%' + @MaxTable + '%' )


    -- while we have columns in the temp table loop through them and put their data into the 
    -- tempdown table
    WHILE @CurrOrdPos IS NOT NULL BEGIN 

        -- get a column name and the data type
        SELECT  @Column = COLUMN_NAME, @DataType = Data_Type
        FROM    tempdb.INFORMATION_SCHEMA.COLUMNS 
        WHERE   Table_Name LIKE '%' + @MaxTable + '%' 
        AND     Ordinal_Position = @CurrOrdPos 


        IF @Column <> 'DIDROCER' BEGIN      -- if it is not the recordid (spelled backward) row from tempwide2 get the row


            IF @DataType IN ( 'image', 'text' ) BEGIN
                -- 'Size of Data: ' + CONVERT(VARCHAR(15), DATALENGTH([NoteText] )) 
                SET @FieldName = @SQ + 'Size of Data: ' + @SQ + ' + CONVERT(VARCHAR(15), DATALENGTH(' + @FieldName + ')) '
            END ELSE BEGIN
                SET @FieldName = 'CAST( [' + @Column + '] AS VARCHAR(7500) )'           -- the fieldname w/ brackets used in SELECT to display the data
            END

            -- build the insert that will put the data into the tempdown table
            SET @SQL = ' INSERT INTO #tempdown ' 
            SET @SQL = @SQL + 'SELECT didrocer ' + @SQ + 'RecordID' + @SQ + ', '        -- recordid field from tempwide2 table
            SET @SQL = @SQL + CONVERT(VARCHAR(10), @CurrOrdPos) + ', '                  -- order of the column
            SET @SQL = @SQL + @SQ + @Column + @SQ + ' ' + @SQ + 'Field' + @SQ + ', '    -- field name 
            SET @SQL = @SQL + @FieldName + @SQ + @Column + @SQ                          -- field data
            SET @SQL = @SQL + ' FROM ' + @MaxTable                                      -- from tempwide2
        END

        --@SQL above looks like this:
        --INSERT INTO #tempdown SELECT DIDROCER 'RecordID', 5, 'UserID' 'Field', [UserID] 'UserID' FROM #tempwide2 {shorten}_____00010000003F
        --PRINT @SQL

        EXEC ( @SQL )       -- run the insert into #tempdown

        -- get the next column pos
        SET @CurrOrdPos = ( SELECT  MIN(Ordinal_Position) 
                            FROM    tempdb.INFORMATION_SCHEMA.COLUMNS 
                            WHERE   Table_Name LIKE '%' + @MaxTable + '%'
                                AND Ordinal_Position > @CurrOrdPos)


    END

    -- display the results VERTICALLY!
    SELECT  ColumnName, Data FROM   #tempdown ORDER BY Rec, Ord, ColumnName

    -- clean up
    IF OBJECT_ID('tempdb..#tempdown') IS NOT NULL DROP TABLE #tempdown
    IF OBJECT_ID('tempdb..#tempwide') IS NOT NULL DROP TABLE #tempwide
    IF OBJECT_ID('tempdb..#tempwide2') IS NOT NULL DROP TABLE #tempwide2

データのテストベッドのセットアップ

--Setup testbed of data
DROP TABLE IF EXISTS dbo.customer
CREATE TABLE [dbo].[Customer] (
    [CustomerID] [int] NULL
    ,[Name] [varchar](30) NULL
    ,[RecordCreated] [datetime] NULL
    ,[RecordUpdated] [datetime] NULL
    ,
    ) ON [PRIMARY]
GO

INSERT [dbo].[Customer] ([CustomerID], [Name], [RecordCreated], [RecordUpdated]) VALUES (1, N'James', CAST(N'2017-11-01T16:16:21.297' AS DateTime), CAST(N'2017-11-01T16:52:02.427' AS DateTime))
GO
INSERT [dbo].[Customer] ([CustomerID], [Name], [RecordCreated], [RecordUpdated]) VALUES (2, N'John', CAST(N'2017-11-01T16:41:52.347' AS DateTime), CAST(N'2017-11-01T16:41:52.347' AS DateTime))
GO
INSERT [dbo].[Customer] ([CustomerID], [Name], [RecordCreated], [RecordUpdated]) VALUES (3, N'Sam', CAST(N'2017-11-01T16:50:25.430' AS DateTime), CAST(N'2017-11-01T16:50:25.430' AS DateTime))
GO
INSERT [dbo].[Customer] ([CustomerID], [Name], [RecordCreated], [RecordUpdated]) VALUES (1, N'James', CAST(N'2017-11-01T16:16:21.297' AS DateTime), CAST(N'2017-11-01T16:52:02.427' AS DateTime))
GO
INSERT [dbo].[Customer] ([CustomerID], [Name], [RecordCreated], [RecordUpdated]) VALUES (2, N'John', CAST(N'2017-11-01T16:41:52.347' AS DateTime), CAST(N'2017-11-01T16:41:52.347' AS DateTime))
GO
INSERT [dbo].[Customer] ([CustomerID], [Name], [RecordCreated], [RecordUpdated]) VALUES (3, N'Sam', CAST(N'2017-11-01T16:50:25.430' AS DateTime), CAST(N'2017-11-01T16:50:25.430' AS DateTime))
GO

以下は、垂直形式で結果を返す実際のプロセスです

IF OBJECT_ID('tempdb..#tempwide') IS NOT NULL DROP TABLE #tempwide

SELECT *
INTO #tempwide -- ADD THIS TO YOUR QUERY
FROM customer a

EXEC sp_SHOWDOWN -- ADD AS THE LAST LINE

これが結果です

| CustomerID    | 1                   |
|---------------|---------------------|
| Name          | James               |
| RecordCreated | Nov  1 2017  4:16PM |
| RecordUpdated | Nov  1 2017  4:52PM |
| CustomerID    | 2                   |
| Name          | John                |
| RecordCreated | Nov  1 2017  4:41PM |
| RecordUpdated | Nov  1 2017  4:41PM |
| CustomerID    | 3                   |
| Name          | Sam                 |
| RecordCreated | Nov  1 2017  4:50PM |
| RecordUpdated | Nov  1 2017  4:50PM |
| CustomerID    | 1                   |
| Name          | James               |
| RecordCreated | Nov  1 2017  4:16PM |
| RecordUpdated | Nov  1 2017  4:52PM |
| CustomerID    | 2                   |
| Name          | John                |
| RecordCreated | Nov  1 2017  4:41PM |
| RecordUpdated | Nov  1 2017  4:41PM |
| CustomerID    | 3                   |
| Name          | Sam                 |
| RecordCreated | Nov  1 2017  4:50PM |
| RecordUpdated | Nov  1 2017  4:50PM |

更新(2018-02-09)

私の回答を投稿した後、 Martin SmithFOR XML PATHといくつかのCROSS APPLYを使用して この問題への素晴らしく単純なアプローチ を参照するコメントを追加しました。

SQL Fiddleリンクが停止した場合に備えて、ここに彼のソリューションを含めます。

SELECT n.value('local-name(.)', 'SYSNAME') AS Col,
       n.value('.', 'nvarchar(4000)')
FROM Customer c
CROSS APPLY 
(SELECT c.*
 FOR XML PATH('r'), TYPE) ca(x)
 CROSS APPLY ca.x.nodes('/r/*') n(n)
4
Scott Hodgin
select v 
from ( values (1), (2), (4)
     ) val(v)
2
paparazzo

_varchar/nvarchar_以外のフィールドを処理していても[Field] + char(13)のシンプルさを求めている場合は、CONCATを使用して_CONVERT/CAST_ボイラープレートをスキップできます(SQL v2012 +) 、投げたものをすべて処理する必要があります

https://docs.Microsoft.com/en-us/sql/t-sql/functions/concat-transact-sql


_SELECT CONCAT(1, char(13), GETDATE(), char(13), 3.0, char(13), 'A')
-------------------------------------------
1
Feb  9 2018 11:39AM
3.0
A

(1 row affected)
_

* CONCATは254個のパラメーターに制限されています

2
Smörgåsbord