SQL Server - قم بتضمين NULL باستخدام unpivot
-
05-07-2019 - |
سؤال
لن يعيد Unpivot Nulls ، لكنني أحتاجها في استعلام مقارنة. أحاول تجنب استخدام Isnull المثال التالي (لأنه في SQL الحقيقي هناك أكثر من 100 حقل:
Select ID, theValue, column_name
From
(select ID,
ISNULL(CAST([TheColumnToCompare] AS VarChar(1000)), '') as TheColumnToCompare
from MyView
where The_Date = '04/30/2009'
) MA
UNPIVOT
(theValue FOR column_name IN
([TheColumnToCompare])
) AS unpvt
أي بدائل؟
المحلول
إنه ألم حقيقي. عليك أن تبديلها قبل UNPIVOT
, ، لأنه لا يوجد صف ينتج عنه ISNULL()
للعمل على - توليد الكود هو صديقك هنا.
لدي مشكلة في PIVOT
كذلك. تتحول الصفوف المفقودة إلى NULL
, ، والتي عليك أن تلتف فيها ISNULL()
على طول الطريق عبر الصف إذا كانت القيم المفقودة هي نفسها مثل 0.0
فمثلا.
نصائح أخرى
للحفاظ على خميرة ، استخدم الصليب الانضمام ... الحالة:
select a.ID, b.column_name
, column_value =
case b.column_name
when 'col1' then a.col1
when 'col2' then a.col2
when 'col3' then a.col3
when 'col4' then a.col4
end
from (
select ID, col1, col2, col3, col4
from table1
) a
cross join (
select 'col1' union all
select 'col2' union all
select 'col3' union all
select 'col4'
) b (column_name)
بدلاً من:
select ID, column_name, column_value
From (
select ID, col1, col2, col3, col4
from from table1
) a
unpivot (
column_value FOR column_name IN (
col1, col2, col3, col4)
) b
يجعل محرر النصوص مع وضع العمود مثل هذه الاستعلامات أسهل في الكتابة. ultraedit لديه ، وكذلك emacs. في emacs يسمى تحرير مستطيلة.
قد تحتاج إلى نصه لـ 100 عمود.
وقعت في نفس المشكلة. استخدام CROSS APPLY
(SQL Server 2005 وما بعده) بدلاً من Unpivot
حل المشكلة. لقد وجدت الحل بناءً على هذه المقالة طريقة بديلة (أفضل؟)وقدمت المثال التالي لإثبات أن Cross Apply لن يتجاهل Nulls مثل Unpivot
.
create table #Orders (OrderDate datetime, product nvarchar(100), ItemsCount float, GrossAmount float, employee nvarchar(100))
insert into #Orders
select getutcdate(),'Windows',10,10.32,'Me'
union
select getutcdate(),'Office',31,21.23,'you'
union
select getutcdate(),'Office',31,55.45,'me'
union
select getutcdate(),'Windows',10,null,'You'
SELECT OrderDate, product,employee,Measure,MeasureType
from #Orders orders
CROSS APPLY (
VALUES ('ItemsCount',ItemsCount),('GrossAmount',GrossAmount)
)
x(MeasureType, Measure)
SELECT OrderDate, product,employee,Measure,MeasureType
from #Orders orders
UNPIVOT
(Measure FOR MeasureType IN
(ItemsCount,GrossAmount)
)AS unpvt;
drop table #Orders
أو ، في SQLServer 2008 بطريقة أقصر:
...
cross join
(values('col1'), ('col2'), ('col3'), ('col4')) column_names(column_name)
لقد وجدت اليسار الخارجي للانضمام إلى النتيجة غير المتوفرة إلى القائمة الكاملة للحقول ، التي يتم سحبها بشكل مناسب من Information_schema ، لتكون إجابة عملية على هذه المشكلة في بعض السياقات.
-- test data
CREATE TABLE _t1(name varchar(20),object_id varchar(20),principal_id varchar(20),schema_id varchar(20),parent_object_id varchar(20),type varchar(20),type_desc varchar(20),create_date varchar(20),modify_date varchar(20),is_ms_shipped varchar(20),is_published varchar(20),is_schema_published varchar(20))
INSERT INTO _t1 SELECT 'blah1', 3, NULL, 4, 0, 'blah2', 'blah3', '20100402 16:59:23.267', NULL, 1, 0, 0
-- example
select c.COLUMN_NAME, Value
from INFORMATION_SCHEMA.COLUMNS c
left join (
select * from _t1
) q1
unpivot (Value for COLUMN_NAME in (name,object_id,principal_id,schema_id,parent_object_id,type,type_desc,create_date,modify_date,is_ms_shipped,is_published,is_schema_published)
) t on t.COLUMN_NAME = c.COLUMN_NAME
where c.TABLE_NAME = '_t1'
</pre>
يشبه الإخراج:
+----------------------+-----------------------+ | COLUMN_NAME | Value | +----------------------+-----------------------+ | name | blah1 | | object_id | 3 | | principal_id | NULL | <====== | schema_id | 4 | | parent_object_id | 0 | | type | blah2 | | type_desc | blah3 | | create_date | 20100402 16:59:23.26 | | modify_date | NULL | <====== | is_ms_shipped | 1 | | is_published | 0 | | is_schema_published | 0 | +----------------------+-----------------------+
باستخدام Dynamic SQL و COALESCE ، قمت بحل المشكلة مثل هذه:
DECLARE @SQL NVARCHAR(MAX)
DECLARE @cols NVARCHAR(MAX)
DECLARE @dataCols NVARCHAR(MAX)
SELECT
@dataCols = COALESCE(@dataCols + ', ' + 'ISNULL(' + Name + ',0) ' + Name , 'ISNULL(' + Name + ',0) ' + Name )
FROM Metric WITH (NOLOCK)
ORDER BY ID
SELECT
@cols = COALESCE(@cols + ', ' + Name , Name )
FROM Metric WITH (NOLOCK)
ORDER BY ID
SET @SQL = 'SELECT ArchiveID, MetricDate, BoxID, GroupID, ID MetricID, MetricName, Value
FROM
(SELECT ArchiveID, [Date] MetricDate, BoxID, GroupID, ' + @dataCols + '
FROM MetricData WITH (NOLOCK)
INNER JOIN Archive WITH (NOLOCK)
ON ArchiveID = ID
WHERE BoxID = ' + CONVERT(VARCHAR(40), @BoxID) + '
AND GroupID = ' + CONVERT(VARCHAR(40), @GroupID) + ') p
UNPIVOT
(Value FOR MetricName IN
(' + @cols + ')
)AS unpvt
INNER JOIN Metric WITH (NOLOCK)
ON MetricName = Name
ORDER BY MetricID, MetricDate'
EXECUTE( @SQL )
Isnull هو نصف الجواب. استخدم Nullif للترجمة مرة أخرى إلى NULL. على سبيل المثال
DECLARE @temp TABLE(
Foo varchar(50),
Bar varchar(50) NULL
);
INSERT INTO @temp( Foo,Bar )VALUES( 'licious',NULL );
SELECT * FROM @temp;
SELECT
Col,
NULLIF( Val,'0Null' ) AS Val
FROM(
SELECT
Foo,
ISNULL( Bar,'0Null' ) AS Bar
FROM
@temp
) AS t
UNPIVOT(
Val FOR Col IN(
Foo,
Bar
)
) up;
هنا أستخدم "0null" كقيمة وسيطة. يمكنك استخدام أي شيء تريده. ومع ذلك ، فإنك تخاطر بالتصادم مع إدخال المستخدم إذا اخترت شيئًا حقيقيًا مثل "NULL". تعمل Garbage بشكل جيد "!@#34 ()) 0" ولكن قد يكون أكثر إرباكًا للمبرمجين في المستقبل. أنا متأكد من أنك تحصل على الصورة.