Obrót przy zmianie typów danych (dynamicznie)

0

Pytanie

Załóżmy, że mam 3 tabele: użytkownicy, niestandardowe atrybuty i wartości atrybutów niestandardowych. Użytkownik może dodawać własne atrybuty, wybierając nazwę i typ atrybutu, a także edytować wartości dla każdego użytkownika.

Oto moi użytkownicy:

ID nazwa nazwisko aktywny data utworzenia Nazwa użytkownika Adres e-mail
3 Ellen Ripley 1 3/25/2235 78439 [email protected]
5 Johnny Rico 1 4/16/2675 Головорез31 [email protected]

atrybuty niestandardowe (mogą być dodawane w dowolnym czasie)

ID imię i nazwisko unikatową nazwę Typ
1 Data Zatrudnienia zatrudnij Data
2 Identyfikator pracownika eeid tel
3 Kierownik kierownik нварчар(50)
4 Wyznaczona Statek wyznaczone miejsce нварчар(50)
5 stanowisko wakat нварчар(50)

typ, który mam obecnie jest jako typ danych sysname.

wartości atrybutów niestandardowych (mogą być zmienione w dowolnym czasie)

ID atrybut identyfikator użytkownika wartość
1 1 3 2335-03-25
2 2 3 78439
3 3 3 Burke, Carter
4 4 3 Сулако
5 5 3 Konsultant
6 1 5 2675-04-16
7 2 5 78440
8 3 5 PORUCZNIK Рашак
9 4 5 Roger Young
10 5 5 Prywatne

wartość, którą ja obecnie mam jak sql_variant typ danych

Więc oto moje pytanie: jak mogę utworzyć raport, w którym będą wyświetlane wszystkie pracownicy i ich atrybuty, na 1 pasku na każdego pracownika, nie wiedząc, ile jest atrybutów niestandardowych, i, co jest szczególnie ważne, chcę wyraźnie przekształcić każda kolumna w odpowiedni typ danych

Pożądany wynik:

nazwa nazwisko data utworzenia Nazwa użytkownika Adres e-mail Data Zatrudnienia Identyfikator pracownika Kierownik Wyznaczona Statek stanowisko
Ellen Ripley 2235-03-25 78439 [email protected] 2335-03-25 78439 Burke, Carter Сулако Konsultant
Johnny Rico 2675-04-16 Головорез31 [email protected] 2675-04-16 78440 PORUCZNIK Рашак Roger Young Prywatne

Ja już nauczyłem się tworzyć dynamiczne nagłówki kolumn za pomocą dynamicznych zapytań, ale ode mnie ucieka konwersja typów.

Ja адаптирую to rozwiązanie dla pól niestandardowych, ale ograniczenia tego rozwiązania polega na tym, że trzeba wiedzieć każde niestandardowe pole, aby wykonać konwersję typów.

Oto co próbowałem. Otrzymałem prawidłowy wynik, za wyjątkiem konwersji typów.

Zapytanie:

DECLARE @columns NVARCHAR(MAX) = '';
DECLARE @sqlcmd NVARCHAR(MAX) = '';

SELECT @columns += QUOTENAME(fullname) + ','
FROM customattributesx ca

ORDER BY ca.id;

SET @columns = LEFT(@columns, LEN(@columns) - 1);

--PRINT @columns;

SET @sqlcmd = '
SELECT * FROM (
    SELECT userid
        ,firstname
        ,lastname
        ,datecreated
        ,username
        ,email
        ,fullname
        ,value
    FROM (
        SELECT u.id as userid
              ,u.firstname
              ,u.lastname
              ,u.datecreated
              ,u.username
              ,u.email
              ,ca.id
              ,ca.fullname as fullname
              ,ca.uniquename
              ,ca.type
              ,cav.value as value
        FROM dbo.users u
        CROSS JOIN customattributesx ca
        INNER JOIN customattributevaluesx cav
            ON cav.attributeid = ca.id AND cav.userid = u.id

        --ORDER BY u.id asc, ca.id asc
    ) t1
) t2
PIVOT (
    MIN(value)
    FOR fullname IN ('+@columns+')
) as pivottable
';
--print @sqlcmd
EXECUTE (@sqlcmd)

Tworzenie Tabel:

USE [CTMS]
GO

/****** Object:  Table [dbo].[users]    Script Date: 11/24/2021 9:29:16 AM ******/
SET ANSI_NULLS ON
GO

SET QUOTED_IDENTIFIER ON
GO

CREATE TABLE IF NOT EXISTS [dbo].[users](
    [id] [int] IDENTITY(1,1) NOT NULL,
    [firstname] [nvarchar](max) NULL,
    [lastname] [nvarchar](max) NULL,
    [active] [bit] NOT NULL,
    [datecreated] [datetime2](7) NOT NULL,
    [username] [nvarchar](256) NULL,
    [email] [nvarchar](256) NULL,
    [emailconfirmed] [bit] NOT NULL,
    [passwordhash] [nvarchar](max) NULL,
    [twofactorenabled] [bit] NOT NULL,
    [lockoutend] [datetimeoffset](7) NULL,
    [eockoutenabled] [bit] NOT NULL,
    [accessfailedcount] [int] NOT NULL,
    [qrcode] [nvarchar](50) NULL,
 CONSTRAINT [PK_id] PRIMARY KEY CLUSTERED 
(
    [id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY],
 CONSTRAINT [uk_email] UNIQUE NONCLUSTERED 
(
    [email] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY],
 CONSTRAINT [uk_qrcode] UNIQUE NONCLUSTERED 
(
    [qrcode] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY],
 CONSTRAINT [uk_username] UNIQUE NONCLUSTERED 
(
    [username] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
GO

ALTER TABLE [dbo].[users] ADD  DEFAULT (getutcdate()) FOR [datecreated]
GO


USE [CTMS]
GO

/****** Object:  Table [dbo].[customattributesx]    Script Date: 11/24/2021 9:31:09 AM ******/
SET ANSI_NULLS ON
GO

SET QUOTED_IDENTIFIER ON
GO

CREATE TABLE IF NOT EXISTS [dbo].[customattributesx](
    [id] [smallint] IDENTITY(1,1) NOT NULL,
    [fullname] [nvarchar](50) NOT NULL,
    [uniquename] [nvarchar](50) NOT NULL,
    [type] [sysname] NOT NULL,
 CONSTRAINT [PK_customattributesx] PRIMARY KEY CLUSTERED 
(
    [id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY],
 CONSTRAINT [uk1_customattributesx] UNIQUE NONCLUSTERED 
(
    [uniquename] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]
GO

USE [CTMS]
GO

/****** Object:  Table [dbo].[customattributevaluesx]    Script Date: 11/24/2021 9:31:27 AM ******/
SET ANSI_NULLS ON
GO

SET QUOTED_IDENTIFIER ON
GO

CREATE TABLE IF NOT EXISTS [dbo].[customattributevaluesx](
    [id] [int] IDENTITY(1,1) NOT NULL,
    [attributeid] [smallint] NOT NULL,
    [userid] [int] NOT NULL,
    [value] [sql_variant] NOT NULL,
 CONSTRAINT [PK_customattributevaluesx] PRIMARY KEY CLUSTERED 
(
    [id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY],
 CONSTRAINT [uk1_customattributevaluesx] UNIQUE NONCLUSTERED 
(
    [attributeid] ASC,
    [userid] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]
GO

ALTER TABLE [dbo].[customattributevaluesx]  WITH CHECK ADD  CONSTRAINT [fk1_customattributesvaluesx] FOREIGN KEY([attributeid])
REFERENCES [dbo].[customattributesx] ([id])
GO

ALTER TABLE [dbo].[customattributevaluesx] CHECK CONSTRAINT [fk1_customattributesvaluesx]
GO

ALTER TABLE [dbo].[customattributevaluesx]  WITH CHECK ADD  CONSTRAINT [fk2_customattributesvaluesx] FOREIGN KEY([userid])
REFERENCES [dbo].[users] ([id])
GO

ALTER TABLE [dbo].[customattributevaluesx] CHECK CONSTRAINT [fk2_customattributesvaluesx]
GO
dynamic pivot sql sql-server
2021-11-24 02:40:38
2

Najlepsza odpowiedź

2

Jeśli chcesz przekonwertować na typ danych (w rzeczywistości może to być warstwa prezentacji), to dynamiczna warunkowa agregacja powinna zrobić swoje.

Przykład

Declare @SQL nvarchar(max) ='
Select U.*' +
(
Select concat(',',quotename(fullname),'=max(case when attributeid=',id,' then try_convert(',type,',value) end)')
 From customattributes
 For XML Path ('')
)+'
 From  users U
 Join  customattributesvalues V on U.ID=V.userid
 Group By U.ID
         ,U.FirstName
         ,U.LastName
         ,U.active
         ,U.datecreated
         ,U.username
         ,U.email
'
--print @SQL
Exec(@SQL)

Wyniki

enter image description here

Wygenerowany SQL Wygląda Następująco

Select U.*
      ,[Hire Date]=max(case when attributeid=1 then try_convert(date,value) end)
      ,[Employee ID]=max(case when attributeid=2 then try_convert(int,value) end)
      ,[Supervisor]=max(case when attributeid=3 then try_convert(nvarchar(50),value) end)
      ,[Assigned Ship]=max(case when attributeid=4 then try_convert(nvarchar(50),value) end)
      ,[Job Title]=max(case when attributeid=5 then try_convert(nvarchar(50),value) end)
 From  #users U
 Join  #customattributesvalues V on U.ID=V.userid
 Group By U.ID
         ,U.FirstName
         ,U.LastName
         ,U.active
         ,U.datecreated
         ,U.username
         ,U.email
2021-11-24 05:15:54

Genialnie! Czy istnieje sposób, aby zmienić to, aby można było korzystać z bitowe typy? Myślę, że mi się uda zakaz typów bitów, ale chciałbym w miarę możliwości zapewnić maksymalną elastyczność.
Tristen Hannah

@TristenHannah Zależy od okoliczności. Jak przechowywane są bity w tabeli wartości? 1/0 lub prawda/fałsz
John Cappelletti

Nie znam prawdziwe/fałszywe оптоном - myślałem, że oni zawsze byli 1/0
Tristen Hannah

@TristenHannah Masz rację odnośnie 1/0. Nie wiem, jak je przechowywano. Chcesz powiedzieć, że try_convert(bit,wartość) nie działa?
John Cappelletti

to operator MAX, który nie działa, i, jak rozumiem, wszystkie agregujące operatorzy nie będą działać. Nie ma jednak powodów do rozpaczy, myślę, że mogę opracować projekt, która zakazuje używania bitowych typów danych.
Tristen Hannah

Rozwiązanie to wydaje się, działa z bitowe typy danych po prostu zmiana dynamicznego wyboru Wybór metody concat(',',funkcje quotename(imię i nazwisko),'=try_convert ('wpisz'Max(przypadek, gdy attributeid='identyfikator, a następnie wartość end))') (czwarta linijka w roztworze)
Tristen Hannah

@TristenHannah Dobra Robota!
John Cappelletti
0

SQL_VARIANT może być wniesiona do docelowego typu danych.

Zmień część dynamicznego kwerendy, w której możesz stworzyć listę kolumn, aby utworzyć dwa listy. Jedna lista jest przeznaczony dla PIVOT część i druga dla SELECT część, w której można wpisać swoje typy danych.

Przykład opiera się na artykule, do którego odwołują się w swoim pytaniu:

DECLARE @PivotList NVARCHAR( MAX )
DECLARE @SelectList NVARCHAR( MAX )
SELECT @SelectList = NULL, @PivotList = NULL
        -- Column list with CAST e.g. CAST( eeid AS INT ) AS eeid
        -- Data types come from your customattributes table
SELECT @SelectList = COALESCE( @SelectList + ',','') + 'CAST( ' + uniquename + ' AS [type] ) AS ' + uniquename,
        -- Just a column list that goes into PIVOT operator
        @PivotList = COALESCE( @PivotList + ',','') + uniquename
-- Your tables for attribute values and attribute type definitions
FROM customattributes AS ca

DECLARE @SQLQuery NVARCHAR(MAX)
SET @SQLQuery =

'SELECT StudID , '+@SelectList+'
FROM
( SELECT SM.StudID, S.SubjectName, SM.Score 
FROM StudentMarks SM 
INNER JOIN Subjects S
ON Sm.SubjectID = S.SubjectID
) AS tbl
PIVOT 
( Sum(Score)
FOR SubjectName IN ('+@PivotList+') ) as pvt'

EXEC(@SQLQuery)
2021-11-24 04:41:32

Witam, śledzę listy wyboru i podsumowania listy, ale sam zapytania SQL, wygląda na to, odwołuje się do innych tabel, które nie są uwzględnione w niego. Jednak widzę, do czego to doprowadzi, i myślę, że mogę wykorzystać tę koncepcję jako możliwe rozwiązanie!
Tristen Hannah

W innych językach

Ta strona jest w innych językach

Русский
..................................................................................................................
Italiano
..................................................................................................................
Română
..................................................................................................................
한국어
..................................................................................................................
हिन्दी
..................................................................................................................
Français
..................................................................................................................
Türk
..................................................................................................................
Česk
..................................................................................................................
Português
..................................................................................................................
ไทย
..................................................................................................................
中文
..................................................................................................................
Español
..................................................................................................................
Slovenský
..................................................................................................................