Get columns only if it is not null is sql server 2012? - sql-server

I have the following rows.I dont want to select those columns whose entire value is null
col1 col2 col3
1 2 NULL
2 3 NULL
3 4 NULL
. . NULL
. . NULL
. . NULL
100 101 NULL
For example,
I want to select only col1 and col2 since all the values of col3 is null ie if col3 contains any value other than null then col1 col2 and col3 should be selected.Other wise col1 and col2 should only be selected.
How to acheive the above scenaria in sqlserver

There could be a better/efficient way present but you can try the below which is mixed o bit TSQL script and dynamic SQL. teasted and it works fine. A sample code below.
create table test1(col1 int,col2 int);
insert into test1(col1) values(2),(3),(4)
declare #col1sum int, #col2sum int, #countrow int;
-- This query will get you NULL count for all columns
select #col1sum = sum(case when col1 is null then 1 else 0 end),
#col2sum = sum(case when col2 is null then 1 else 0 end),
#countrow = count(1)
from test1;
select #col1sum, #col2sum, #countrow; --for display
declare #sql varchar(100);
-- check for column null count < total count of rows
if(#col1sum < #countrow)
set #sql = 'select col1 from test1';
else if(#col2sum < #countrow)
set #sql = 'select col2 from test1';
else
set #sql = 'select col1,col2 from test1';
exec(#sql); -- Finally execute the dynamic sql

If you want to go the way that David suggests in the comments and do the hiding in the UI, but have the database do the work to help you out, you can use aggregate functions with windows to add some extra columns:
declare #t table (col1 int,col2 int,col3 int)
insert into #t(col1,col2,col3) values
(1 ,2 ,NULL),
(2 ,3 ,NULL),
(3 ,4 ,NULL),
(9 ,NULL ,NULL),
(100 ,101 ,NULL)
select
col1,CASE WHEN MAX(col1) OVER () IS NULL THEN 0 ELSE 1 END as col1_HasValues,
col2,CASE WHEN MAX(col2) OVER () IS NULL THEN 0 ELSE 1 END as col2_HasValues,
col3,CASE WHEN MAX(col3) OVER () IS NULL THEN 0 ELSE 1 END as col3_HasValues
from #t
Which produces the result set:
col1 col1_HasValues col2 col2_HasValues col3 col3_HasValues
----------- -------------- ----------- -------------- ----------- --------------
1 1 2 1 NULL 0
2 1 3 1 NULL 0
3 1 4 1 NULL 0
9 1 NULL 1 NULL 0
100 1 101 1 NULL 0
The _HasValues columns will be identical across all rows and tells you if any row has a non-NULL value in the preceding column. If it's 0, you should hide the column in the UI.

Related

SQL SERVER Pivot rows to column

I have the input as
123 1 Y Active
123 1 Y Idle
109 1 Y Active
109 1 Y Away
165 1 Y Active
145 1 Y Idle
I need the output as :
123 1 Y Active and Idle
109 1 Y Active and Away
165 1 Y Active Only
145 1 Y Idle Only
SELECT distinct COl1,Col2,Col3 ,
case when Col4 = 'Active' and Col4 = 'Idle' then 'Active and Idle'
when Col4 = 'Active' and Col4 = 'Away' then 'Active and Away'
when Col4 = 'Active' then 'Active Only'
when Col4 = 'Idle' then 'Idle Only'
end as category
from Person
I tried the above query but it is not working and it wont work because i am
trying to convert multiple rows to 1 column, which is not possible with
Case.
I have to try something like PIVOT, but not able to implement . Any help?
You need FOR XML PATH() approach :
SELECT DISTINCT p.col1, STUFF(pp.Col4, 1, 4, '') AS category
FRPM Person p CROSS APPLY
( SELECT ' AND ' + p1.Col4
FROM Person p1
WHERE P1.col1 = p.col1
FOR XML PATH('')
) pp(Col4);
By this way FOR XML PATH('') would return xml element for each col1 & outer query STUFF() will replace AND with '' at the start of position of string.
try the following:
declare #table table(
col1 int,col2 int,col3 nvarchar(10),col4 nvarchar(50)
)
insert into #table values(123, 1, 'Y' ,'Active')
insert into #table values(123, 1, 'Y' ,'Idle')
insert into #table values(109, 1, 'Y' ,'Active')
insert into #table values(109, 1, 'Y' ,'Away')
insert into #table values(165, 1, 'Y' ,'Active')
insert into #table values(145, 1, 'Y' ,'Idle')
select col1,col2,col3, stuff((SELECT ' AND ' + t2.col4 + CASE WHEN count(t1.col1) = 1 THEN ' ONLY' ELSE '' END
FROM #table t2
WHERE t2.col1 = t1.col1
ORDER BY t2.col1
FOR XML PATH(''), TYPE).value('.', 'varchar(max)')
,1,5,'') AS col4
from #table t1
group by col1,col2,col3
I'm using the stuff to concat values that does not include in the group.
And when count = 1 its mean that there is only one item in this group so you need to add the Only suffix.
That's not such an obvious question, even in SQL Server 2017 which provides STRING_AGG. A check is needed to append Only if there is only one row per group.
Given this table :
declare #table table (id int, flag1 int, flag2 char(1), status varchar(40))
insert into #table
values
(123,1,'Y','Active'),
(123,1,'Y','Idle'),
(109,1,'Y','Active'),
(109,1,'Y','Away'),
(165,1,'Y','Active'),
(145,1,'Y','Idle')
The following query will return the aggregated values. If there's only one value, it will append Only :
select id,flag1,flag2,
string_agg(status,' and ') + IIF(count(*)>1,'',' Only')
from #table
group by id,flag1,flag2
This produces :
109 1 Y Active and Away
123 1 Y Active and Idle
145 1 Y Idle Only
165 1 Y Active Only

How to check what column in INSERT do not have the correct data type?

Imagine I have 200 columns in one INSERT statement, and I occasionally get an "Cannot convert" error for one of columns. Things is, I do not know which column causes this error.
Is there any way in T-SQL or mybatis to check WHICH column has the incorrect format? (I have just date, char, numeric). I can use ISNUMERIC, ISDATE for every column, but this is not so elegant.
I'm using mybatis in Java, so I cannot use any PreparedStatement or so.
You could build a query that tries to convert each of the suspected columns.
And limit the query to where one of the attempts to convert fails.
Mostly the bad data will be in CHAR's or VARCHAR's when trying to cast or convert them to a datetime or number type.
So you can limit your research to those.
Also, from the error you should see which value failed to convert to which type. Which can also help to limit which fields you research.
A simplified example using table variables:
declare #T1 table (id int identity(1,1) primary key, field1 varchar(30), field2 varchar(30), field3 varchar(30));
declare #T2 table (id int identity(1,1) primary key, field1_int int, field2_date date, field3_dec decimal(10,2));
insert into #T1 (field1, field2, field3) values
('1','2018-01-01','1.23'),
('not an int','2018-01-01','1.23'),
('1','not a date','1.23'),
('1','2018-01-01','not a decimal'),
(null,'2018-01-01','1.23'),
('1',null,'1.23'),
('1','2018-01-01',null)
;
select top 1000
id,
case when try_convert(int, field1) is null then field1 end as field1,
case when try_convert(date, field2) is null then field2 end as field2,
case when try_convert(decimal(10,4), field3) is null then field3 end as field3
from #T1
where
try_convert(int, coalesce(field1, '0')) is null
or try_convert(date, coalesce(field2, '1900-01-01')) is null
or try_convert(decimal(10,4), coalesce(field3, '0.0')) is null;
Returns:
id field1 field2 field3
-- ---------- ----------- -------------
2 not an int NULL NULL
3 NULL not a date NULL
4 NULL NULL not a decimal
If the origin data doesn't have to much bad data you could try to fix the origin data first.
Or use the try_convert for the problematic columns with bad data.
For example:
insert into #T2 (field1_int, field2_date, field3_dec)
select
try_convert(int, field1),
try_convert(date, field2),
try_convert(decimal(10,4), field3)
from #T1;
With larger imports - especially when you expect issues - a two-stepped approach is highly recommended.
import the data to a very tolerant staging table (all NVARCHAR(MAX))
check, evaluate, manipulate, correct whatever is needed and do the real insert from here
Here is a generic approach you might adapt to your needs. It will check all tables values against a type-map-table and output all values, which fail in TRY_CAST (needs SQL-Server 2012+)
A table to mockup the staging table (partly borrowed from LukStorms' answer - thx!)
CREATE TABLE #T1 (id INT IDENTITY(1,1) PRIMARY KEY
,fldInt VARCHAR(30)
,fldDate VARCHAR(30)
,fldDecimal VARCHAR(30));
GO
INSERT INTO #T1 (fldInt, fldDate, fldDecimal) values
('1','2018-01-01','1.23'),
('blah','2018-01-01','1.23'),
('1','blah','1.23'),
('1','2018-01-01','blah'),
(null,'2018-01-01','1.23'),
('1',null,'1.23'),
('1','2018-01-01',null);
--a type map (might be taken from INFORMATION_SCHEMA of an existing target table automatically)
DECLARE #type_map TABLE(ColumnName VARCHAR(100),ColumnType VARCHAR(100));
INSERT INTO #type_map VALUES('fldInt','int')
,('fldDate','date')
,('fldDecimal','decimal(10,2)');
--The staging table's name
DECLARE #TableName NVARCHAR(100)='#T1';
--dynamically created statements for each column
DECLARE #columnSelect NVARCHAR(MAX)=
(SELECT
' UNION ALL SELECT id ,''' + tm.ColumnName + ''',''' + tm.ColumnType + ''',' + QUOTENAME(tm.ColumnName)
+ ',CASE WHEN TRY_CAST(' + QUOTENAME(tm.ColumnName) + ' AS ' + tm.ColumnType + ') IS NULL THEN 0 ELSE 1 END ' +
'FROM ' + QUOTENAME(#TableName)
FROM #type_map AS tm
FOR XML PATH('')
);
-The final dynamically created statement
DECLARE #cmd NVARCHAR(MAX)=
'SELECT tbl.*
FROM
(
SELECT 0 AS id,'''' AS ColumnName,'''' AS ColumnType,'''' AS ColumnValue,0 AS IsValid WHERE 1=0 '
+ #columnSelect +
') AS tbl
WHERE tbl.IsValid = 0;'
--Execution with EXEC()
EXEC(#cmd);
The result:
+----+------------+---------------+-------------+---------+
| id | ColumnName | ColumnType | ColumnValue | IsValid |
+----+------------+---------------+-------------+---------+
| 2 | fldInt | int | blah | 0 |
+----+------------+---------------+-------------+---------+
| 5 | fldInt | int | NULL | 0 |
+----+------------+---------------+-------------+---------+
| 3 | fldDate | date | blah | 0 |
+----+------------+---------------+-------------+---------+
| 6 | fldDate | date | NULL | 0 |
+----+------------+---------------+-------------+---------+
| 4 | fldDecimal | decimal(10,2) | blah | 0 |
+----+------------+---------------+-------------+---------+
| 7 | fldDecimal | decimal(10,2) | NULL | 0 |
+----+------------+---------------+-------------+---------+
The statement created is like here:
SELECT tbl.*
FROM
(
SELECT 0 AS id,'' AS ColumnName,'' AS ColumnType,'' AS ColumnValue,0 AS IsValid WHERE 1=0
UNION ALL SELECT id
,'fldInt'
,'int'
,[fldInt]
,CASE WHEN TRY_CAST([fldInt] AS int) IS NULL THEN 0 ELSE 1 END
FROM [#T1]
UNION ALL SELECT id
,'fldDate'
,'date',[fldDate]
,CASE WHEN TRY_CAST([fldDate] AS date) IS NULL THEN 0 ELSE 1 END
FROM [#T1]
UNION ALL SELECT id
,'fldDecimal'
,'decimal(10,2)'
,[fldDecimal]
,CASE WHEN TRY_CAST([fldDecimal] AS decimal(10,2)) IS NULL THEN 0 ELSE 1 END
FROM [#T1]
) AS tbl
WHERE tbl.IsValid = 0;

How to select distinct rows, but repeat if it has a different row between the equal ones

Having data like this:
id text bit date
1 row 1 2016-11-24
2 row 1 2016-11-25
3 row 0 2016-11-26
4 row 1 2016-11-27
I want to select the data based on where the text and bit columns are distinct, but based on some order, in this case the id, the data changes between two identical rows, it should duplicate this row on the selection.
So, if I use distinct on SQL, I would get rows 1 and 3, but I want to retreive rows 1, 3 and 4, because even 1 and 4 being identical, row 3 is between then when ordering by id.
With a larger dataset, like:
id text bit date
1 row 1 2016-11-24
2 row 1 2016-11-25
3 row 0 2016-11-26
4 row 1 2016-11-27
5 foo 1 2016-11-28
6 bar 1 2016-11-29
7 row 1 2016-11-30
8 row 0 2016-12-01
9 row 0 2016-12-02
10 row 1 2016-12-03
Again, selecting with distinct on text and bit columns, the query would retrieve rows 1,3,5 and 6, but actually I want rows 1,3,4,5,6,7,8 and 10.
;with tb(id,[text],[bit],[date]) AS (
SELECT 1,'row',1,'2016-11-24' union
SELECT 2,'row',1,'2016-11-25' union
SELECT 3,'row',0,'2016-11-26' union
SELECT 4,'row',1,'2016-11-27' union
SELECT 5,'foo',1,'2016-11-28' union
SELECT 6,'bar',1,'2016-11-29' union
SELECT 7,'row',1,'2016-11-30' union
SELECT 8,'row',0,'2016-12-01' union
SELECT 9,'row',0,'2016-12-02' union
SELECT 10,'row',1,'2016-12-03')
select t1.* from tb as t1
OUTER APPLY (select top 1 [text],[bit] from tb as tt where tt.id<t1.id order by id desc ) as t2
where t1.[text]!=isnull(t2.[text],'') or t1.[bit]!=isnull(t2.[bit],1-t1.[bit])
result set:
1 row 1 2016-11-24
3 row 0 2016-11-26
4 row 1 2016-11-27
5 foo 1 2016-11-28
6 bar 1 2016-11-29
7 row 1 2016-11-30
8 row 0 2016-12-01
10 row 1 2016-12-03
It seems that you need a row-by-row operator. You need to know if the new row is the same as the previous one or not. If it is, neglect it, if not, keep it. Here is my solution:
declare #text varchar(100)=(select [text] from Mytable where id = 1)
declare #bit bit = (select [bit] from Mytable where id = 1)
declare #Newtext varchar(100)
declare #Newbit bit
declare #Mytable table(id int, [text] varchar(100), [bit] bit)
Insert into #Mytable select id,text, bit from Mytable where id = 1
declare #counter int =2
while #counter<=(select COUNT(*) from MyTable)
Begin
select #Newtext=(select [text] from Mytable where id = #counter)
select #Newbit=(select [bit] from Mytable where id = #counter)
IF #Newtext!=#text or #Newbit!=#bit
Begin
Insert into #Mytable
select * from Mytable where id = #counter
End
set #text = #Newtext
set #bit = #Newbit;
set #counter = #counter+1
END
select * from #Mytable

Check if columns are NULL or contains NULL in table. - MS SQL Server

Following is my table (TestTable) where Column_3 is NULL.
Column_1 Column_2 Column_3
-------- -------- --------
1 2 NULL
1 3 NULL
5 6 NULL
As per functionality, user can select one or more columns.
For example, if user selects Column_3 & Column_2 where Column_3 is NULL. I want to tell user that Column_3 is NULL.
Query : Works for single column
if exists(select * from TestTable where Column_3 is null)
print 'Yes'
else
print 'No'
Result :
Yes
Query : For multiple Columns (not working)
declare #columns nvarchar(max), #tableName nvarchar(max), #query nvarchar(max)
set #columns = 'Column_3, Column_2'
set #tableName = 'TestTable'
set #query = 'select * from (select ' + #columns + ' from ' + #tableName + ') as Result'
print #query
EXEC SP_EXECUTESQL #query
Here
#query = select * from (select Column_3, Column_2 from TestTable) as Result
Above query gives me result for those two columns. I'm not sure how can I check NULL in this query for multiple columns. If I add IS NULL (like I did for single column) after or before last parenthesis it gives me Incorrect syntax near the keyword 'is'. error. How can I achieve my goal in this situation?
Two Solutions (Column is All NULLs, Column Contains Some NULLs)
I have slightly altered your original example in order to provide two solutions:
Column_1 Column_2 Column_3
-------- -------- --------
1 2 NULL
1 NULL NULL
5 6 NULL
First, test for NULLs and count them:
select
sum(case when Column_1 is null then 1 else 0 end) as Column_1,
sum(case when Column_2 is null then 1 else 0 end) as Column_2,
sum(case when Column_3 is null then 1 else 0 end) as Column_3,
from TestTable
Yields a count of NULLs:
Column_1 Column_2 Column_3
0 1 3
Where the result is 0, there are no NULLs.
Second, let's count the non-NULLs:
select
sum(case when Column_1 is null then 0 else 1 end) as Column_1,
sum(case when Column_2 is null then 0 else 1 end) as Column_2,
sum(case when Column_3 is null then 0 else 1 end) as Column_3,
from TestTable
...But because we're counting non-NULLs here, this can be simplified to:
select
count(Column_1) as Column_1,
count(Column_2) as Column_2,
count(Column_3) as Column_3,
from TestTable
Either one yields:
Column_1 Column_2 Column_3
3 2 0
Where the result is 0, the column is entirely made up of NULLs.
If you only need to check a given column, then TOP 1 is quicker because it should stop at the first hit:
select count(*) from (select top 1 'There is at least one NULL' AS note from TestTable where Column_3 is NULL) a
0 = There are no NULLs, 1 = There is at least one NULL
SELECT COUNT(*) FROM (SELECT TOP 1 'There is at least one non-NULL' AS note FROM sat_data_active_season_group WHERE season_group IS NOT NULL) a
0 = They are all NULL, 1 = There is at least one non-NULL
I hope this helps.
we can check with the help of IN like
...WHERE NULL IN (Column_2, Column_3)
from your comment Well the multiple column will be Column_3, Column_2 in format
might be this is helpful for you
select * from (select Column_3, Column_2 from #temp where null in (Column_3, Column_2)) as Result
Try as below.
You can find the null able column by using CASE.
Select CASE WHEN Column_3 IS NULL THEN 'Column 3 is null' ELSE Column_3 END as Column3,
CASE WHEN Column_2 IS NULL THEN 'Column 2 is null' ELSE Column_2 END as Column2
From TableName
It sounds like you need a CASE statement. Reference
Example:
SELECT CASE
WHEN ISNULL(Column_3) THEN -- do something
WHEN NOT ISNULL(Column_3) THEN -- do something else
AS Column_3 -- or some other name

How to get the true/false count from a bit field into two separate columns

I need to create a query that will sum the number of True(1) and False(0) into two separate columns from one bit field.
I'm joining 3 tables and need it to be something like:
Attribute | Class | Pass | Fail
I will be grouping on Attribute and Class.
Something like this:
SUM(CASE WHEN ColumnName = 1 THEN 1 ELSE 0 END) AS Pass,
SUM(CASE WHEN ColumnName = 0 THEN 1 ELSE 0 END) AS Fail
This works (at least in SQL 2008)
SELECT SUM(Passed + 0) PASS , SUM(1 - Passed) FAIL
I am adding 0 to Passed in the first sum as a short hand way of converting from bit to int since you can't sum bits directly.
try:
declare #table table (columnName bit)
insert into #table values (1)
insert into #table values (1)
insert into #table values (1)
insert into #table values (1)
insert into #table values (1)
insert into #table values (0)
insert into #table values (0)
insert into #table values (0)
insert into #table values (0)
SELECT
SUM(CASE WHEN ColumnName = 1 THEN 1 ELSE 0 END) AS True1
, SUM(CASE WHEN ColumnName = 0 THEN 1 ELSE 0 END ) AS False0
from #Table
OUTPUT:
True1 False0
----------- -----------
5 4
(1 row(s) affected)
SELECT
Attribute,
Class,
SUM(CASE BitField WHEN 1 THEN 1 ELSE 0 END) AS [Pass],
SUM(CASE BitField WHEN 0 THEN 1 ELSE 0 END) AS [Fail]
FROM
Table
GROUP BY
Attribute,
Class
Another option would be
SELECT Attribute, Class
COUNT(CASE WHEN ColumnName = 1 THEN 1 END) Pass,
COUNT(CASE WHEN ColumnName = 0 THEN 1 END) Fail FROM YourTable
GROUP BY Attribute, Class
there is even one more option:
SELECT
Attribute,
Class,
COUNT(BoolColumnName = 1 or NULL) Pass,
COUNT(BoolColumnName = 0 or NULL) Fail
FROM Table
GROUP BY Attribute, Class

Resources