Comparing columns containing NULLS - sql-server

Suppose I have this table:
CREATE TABLE t (id int, cola int, colb int);
INSERT INTO t VALUES (1, 1, 1), (2, 1, 2), (3, 1, NULL), (4, NULL, 4);
id cola colb
1 1 1 1
2 2 1 2
3 3 1 NULL
4 4 NULL 4
I want to find the rows where cola does not equal colb. I would expect rows 2-4 to be returned when I write:
SELECT * FROM t WHERE cola != colb
But instead only the second row is returned. In order to pull rows 2-4 I have to write:
SELECT * FROM t
WHERE cola != colb
OR (cola IS NULL AND colb IS NOT NULL)
OR (cola IS NOT NULL AND colb IS NULL)
Is there a cleaner way to do this?
Tested here: http://rextester.com/VJYFX19301

probably the easiest way would be to use an isnull() wrapper, say:
SELECT * FROM t WHERE isnull(cola, -1) != isnull(colb, -1)
assuming this is a fairly small table. there are some potential performance caveats to joining on a function, as this is doing, but maybe in your case it's ok?

Related

SQLServer Order By IGNORE NULL values

For some reason I can't change the table or update the data for now. Here the problem :
I have menu_user table below :
userID menuID
(null) 2
(null) 3
1 3
2 1
3 2
4 5
5 0
userID and menuID not duplicated. The problem is how to ORDER BY userID, menuID but when userID has NULL value, it will look for another row that has same menuID and place it after this row. menuID just have max 2 same value and if it have, another one must be NULL
The order result expected :
userID menuID
1 3
(null) 3
2 1
3 2
(null) 2
4 5
5 0
Here the script sample :
CREATE TABLE [dbo].[menu_user](
[userID] [int] NULL,
[menuID] [int] NULL
);
INSERT [dbo].[menu_user] ([userID], [menuID]) VALUES (NULL, 3);
INSERT [dbo].[menu_user] ([userID], [menuID]) VALUES (1, 3);
INSERT [dbo].[menu_user] ([userID], [menuID]) VALUES (2, 1);
INSERT [dbo].[menu_user] ([userID], [menuID]) VALUES (3, 2);
INSERT [dbo].[menu_user] ([userID], [menuID]) VALUES (4, 5);
INSERT [dbo].[menu_user] ([userID], [menuID]) VALUES (5, 0);
INSERT [dbo].[menu_user] ([userID], [menuID]) VALUES (NULL, 2);
ADDED
If possible I want this script as View (just SELECT with No Variable).
This seems to do the trick. You need to do something to relate multiple rows together. Here I've chosen to use a left join:
select
m1.*
from
menu_user m1
left join
menu_user m2
on
m1.userID is null and
m1.menuID = m2.menuID and
m2.userID is not null
order by
COALESCE(m1.userID,m2.userID),m1.userID desc
Result:
userID menuID
----------- -----------
1 3
NULL 3
2 1
3 2
NULL 2
4 5
5 0
Hopefully you can see how it's achieving its aims.
Check this, ordering is bit messed up but this gives you your desired result.
SELECT * FROM menu_user mu
ORDER BY mu.menuID,
CASE WHEN mu.userID IS NULL THEN mu.menuID END
Using the left join solution will produce duplicates when there are more than 1 non-null Users for a menuID. This is another method.
select userID, menuID
From (
select *, Case when a.UseriD is not null then cast(a.userID as float) else
(select max(b.userID) + 0.1 from menu_user b where a.menuID = b.menuID and a.userID is null) end as SortCol
from menu_user a
) c Order by SortCol
Try this query:
SELECT *
FROM menu_user mu
WHERE userID IS NOT NULL
ORDER BY mu.menuID

Unpivot the columns into rows

I have one row with an unknown number of columns. I need to unpivot all those columns into one column. Here is an example. I start with this table
select 1 as c1, 2 as c2, 3 as c3
into tempP
If we select this table the result will be
c1 c2 c3
1 2 3
I want the result to be this
new_name
1
2
3
Also, remember that I don't know if I will have 3 columns. I might have 100 columns, but the number of rows will always be 1. Thanks.
As Bluefeet mentioned in your comments section you will need an UNPIVOT query for this not a Pivot query.
One thing you need to be careful about, the data type should be the same for all the columns you are Unpivoting.
Something like this....
DECLARE #TABLE TABLE(c1 INT , c2 INT, c3 INT)
INSERT INTO #TABLE VALUES
(1, 2, 3),
(10, 20, 30)
SELECT NewColumn
FROM #TABLE
UNPIVOT (NewColumn FOR allcols IN ([c1],[c2],[c3]) ) up
Result:
NewColumn
1
2
3
10
20
30

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

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.

SQL Server: How to write a query to select id having all values less than 0(zero)

Consider the following table,
Table 1:
id (int): 1 1 2 2 2
value (int): -1 0 -1 -3 -8
How to write a query to select the id from the table which has all the values of column value less than 0?
Try this one -
DECLARE #temp TABLE
(
id INT,
value INT
)
INSERT INTO #temp (id, value)
VALUES (1, -1), (1, 0), (2, -1), (2, -3), (2, -8)
SELECT id
FROM #temp
GROUP BY id
HAVING MAX(value) < 0
Output -
id
-----------
2

Tsql group by clause with exceptions

I have a problem with a query.
This is the data (order by Timestamp):
Data
ID Value Timestamp
1 0 2001-1-1
2 0 2002-1-1
3 1 2003-1-1
4 1 2004-1-1
5 0 2005-1-1
6 2 2006-1-1
7 2 2007-1-1
8 2 2008-1-1
I need to extract distinct values and the first occurance of the date. The exception here is that I need to group them only if not interrupted with a new value in that timeframe.
So the data I need is:
ID Value Timestamp
1 0 2001-1-1
3 1 2003-1-1
5 0 2005-1-1
6 2 2006-1-1
I've made this work by a complicated query, but am sure there is an easier way to do it, just cant think of it. Could anyone help?
This is what I started with - probably could work with that. This is a query that should locate when a value is changed.
> SELECT * FROM Data d1 join Data d2 ON d1.Timestamp < d2.Timestamp and
> d1.Value <> d2.Value
It probably could be done with a good use of row_number clause but cant manage it.
Sample data:
declare #T table (ID int, Value int, Timestamp date)
insert into #T(ID, Value, Timestamp) values
(1, 0, '20010101'),
(2, 0, '20020101'),
(3, 1, '20030101'),
(4, 1, '20040101'),
(5, 0, '20050101'),
(6, 2, '20060101'),
(7, 2, '20070101'),
(8, 2, '20080101')
Query:
;With OrderedValues as (
select *,ROW_NUMBER() OVER (ORDER By TimeStamp) as rn --TODO - specific columns better than *
from #T
), Firsts as (
select
ov1.* --TODO - specific columns better than *
from
OrderedValues ov1
left join
OrderedValues ov2
on
ov1.Value = ov2.Value and
ov1.rn = ov2.rn + 1
where
ov2.ID is null
)
select * --TODO - specific columns better than *
from Firsts
I didn't rely on the ID values being sequential and without gaps. If that's the situation, you can omit OrderedValues (using the table and ID in place of OrderedValues and rn). The second query simply finds rows where there isn't an immediate preceding row with the same Value.
Result:
ID Value Timestamp rn
----------- ----------- ---------- --------------------
1 0 2001-01-01 1
3 1 2003-01-01 3
5 0 2005-01-01 5
6 2 2006-01-01 6
You can order by rn if you need the results in this specific order.

Resources