MSSQL select lowest but not NULL/zero value from 25 columns - sql

I have 25 (numeric) columns in one table in MSSQL and I need select lowest value, but not NULL or 0 value.
Columns are named like "%_price" (aaa_price, bbb_price, ccc_price...).
Some columns contains 0 or NULL value.
Example:
table (aaa_price, bbb_price, ccc_price, ddd_price, eee_price, fff_price)
value (NULL, 0, 324.23, 162.50, NULL, 1729.72 )
Right result:
162.50
I can use some "brute force" method like:
SELECT CASE
WHEN Col1 <= Col2 AND Col1 <= Col3 AND Col1 <= Col4 AND Col1 <= Col5 THEN Col1
WHEN Col2 <= Col3 AND Col2 <= Col4 AND Col2 <= Col5 THEN Col2
WHEN Col3 <= Col4 AND Col3 <= Col5 THEN Col3
WHEN Col4 <= Col5 THEN Col4
ELSE Col5
END AS [Min Value] FROM [Your Table]
But its insane with 25 columns... is there any better solution?
Thank You!

Cross apply can be good option in this case:
select
*
from
myTable
cross apply (select
minVal = min(val)
from (
values (aaa_price),(bbb_price),(...)
) t(val) where val > 0
) q
Edit:
You have to use dynamic SQL if you want to get column names from INFORMATION_SCHEMA.COLUMNS table.
declare #sql varchar(8000)
declare #cols varchar(8000)
select #cols =
stuff((
SELECT
',(' + COLUMN_NAME + ')'
FROM
INFORMATION_SCHEMA.COLUMNS
WHERE
TABLE_NAME = 'mytable'
AND TABLE_SCHEMA='dbo'
AND COLUMN_NAME LIKE '%price'
for xml path('')
), 1, 1, '')
set #sql = 'select
*
from
mytable
cross apply (select
minVal = min(val)
from (
values ' + #cols + '
) t(val) where val > 0
) q'
exec (#sql)

You can create a dynamic SQL statement and execute it in the following form
declare #tablename sysname = 'MultipleNumericColumns'
declare #sql nvarchar(max)
select #sql = isnull(#sql + ' union all ','') + '
SELECT ' + name + ' as colname from ' + #tablename
from sys.all_columns
where
object_id = OBJECT_ID(#tablename)
set #sql = '
select min(colname)
from (
' + #sql + '
) t
where colname > 0'
EXECUTE(#sql)
You can realize that first I get the column names from system view
You can exclude columns that you don't want or use a pattern like name like '%price% etc at this step
Then I build a dynamic SQL query into a string variable as sql command
Please note that I use WHERE clause for greater than 0, etc
Final step is execution with EXECUTE command

Use UNPIVOT
SELECT Min(VALUE)
FROM (
SELECT Col1, Col2, Col3...
FROM YourTable
) t
UNPIVOT (VALUE FOR ITEM IN (Col1, Col2, Col3...)) u
WHERE VALUE != 0

Related

string or binary data would be truncated. in sql server

How to overcome this?
I have insert statements for large tables. there were lot of columns in those tables.
Is there any simple way to find the column which is causing above error?
The simplest way is to increase the length of the column which is giving you the error "string or binary data would be truncated. in sql server".
Now to find which column is giving that error you can check How to find what column caused the String or binary data would be truncated message
First thing we need to know is what the columns are in the table that are (n)chars or (n)varchar
select column_name
from information_schema.columns
where table_name = 'temp'
and data_type in('varchar','char','nvarchar','nchar')
Output:
column_name
————–
Col1
Col2
Col3
Col4
Col5
That was easy, now we want to know the max length of the data in each column
declare #sql varchar(8000)
select #sql = 'select 0 as _col0 ,'
select #sql += 'max(len( ' + column_name+ ')) AS ' + column_name + ','
from information_schema.columns
where table_name = 'temp'
and data_type in('varchar','char','nvarchar','nchar')
select #sql = left(#sql,len(#sql) -1)
select #sql +=' into MaxLengths from temp'
--select #sql -debugging so simple, a caveman can do it
exec (#sql)
That code basically creates and runs the following
select 0 as _col0 ,
max(len( Col1)) AS Col1,
max(len( Col2)) AS Col2,
max(len( Col3)) AS Col3,
max(len( Col4)) AS Col4,
max(len( Col5)) AS Col5
into MaxLengths
from temp
If we now look in the MaxLengths table we will see the following
select * from MaxLengths
_col0 Col1 Col2 Col3 Col4 Col5
---------------------------------------------------
0 13 20 6 4 15
Next to figure out is what the max length of the column itself is in the table that we want to insert into
Run the following query
select character_maximum_length,column_name
from information_schema.columns
where table_name = 'TestTrunc'
and data_type in('varchar','char','nvarchar','nchar')
Result
character_maximum_length column_name
--------------------------------------------
10 Col1
15 Col2
20 Col3
3 Col4
10 Col5
We will again do this dynamically and insert the values into another table
declare #sql varchar(8000)
select #sql = 'select 0 as _col0, '
select #sql += '' + convert(varchar(20),character_maximum_length)+ ' AS ' + column_name + ','
from information_schema.columns
where table_name = 'TestTrunc'
and data_type in('varchar','char','nvarchar','nchar')
select #sql = left(#sql,len(#sql) -1)
select #sql +=' into TempTrunc '
--select #sql -debugging so simple, a caveman can do it
exec (#sql)
Now we can see what we have in the two tables
select 'TempTrunc' as TableNAme,* from TempTrunc
union all
select 'MaxLengths' as TableNAme,* from MaxLengths
TableNAme _col0 Col1 Col2 Col3 Col4 Col5
-------------------------------------------------------------
TempTrunc 0 10 15 20 3 10
MaxLengths 0 13 20 6 4 15
As you can see, all columns except for Col3 will cause the truncation problem
Of course we want to do something like this, it will tell us which columns have truncation problems
select case when t.col1 > tt.col1 then 'truncation' else 'no truncation' end as Col1,
case when t.col2 > tt.col2 then 'truncation' else 'no truncation' end as Col2,
case when t.col3 > tt.col3 then 'truncation' else 'no truncation' end as Col3,
case when t.col4 > tt.col4 then 'truncation' else 'no truncation' end as Col4,
case when t.col5 > tt.col5 then 'truncation' else 'no truncation' end as Col5
from MaxLengths t
join TempTrunc tt on t._col0 = tt._col0
Col1 Col2 Col3 Col4 Col5
------------------------------------------------------------------------------------
truncation truncation no truncation truncation truncation
Source
I believe you already know that this is related with inserting a string value into a column with less size
You can query table columns from know table names and compare them.
select
OBJECT_NAME(object_id),
name,
max_length,
precision,
scale
from sys.columns where object_id in (
select object_id from sys.tables where name in ('tbl','Emp')
)

Remove a ROW of NULL entries SQl Server

If I have a table which looks like this
Is there a way to remove all rows in a SQL tablw without knowing the column titles, if each item in that row is NULL?
Thanks!
Maybe there is another way but since you've tagged SqlCommand i would use ADO.NET. You can use dataReader.GetSchemaTable to retrieve column informations only:
// retrieve column names only
DataTable schema = null;
using (var con = new SqlConnection(Properties.Settings.Default.ConnectionString))
{
using (var schemaCommand = new SqlCommand("SELECT * FROM dbo.TableName", con))
{
con.Open();
using (var reader = schemaCommand.ExecuteReader(CommandBehavior.SchemaOnly))
{
schema = reader.GetSchemaTable();
}
}
}
// build sql with parameters dynamically
var columnQuery = schema.AsEnumerable()
.Select(r => string.Format("{0} IS NULL", r.Field<String>("ColumnName")));
string where = string.Join(" AND ", columnQuery);
string sql = string.Format("DELETE FROM dbo.TableName WHERE {0}", where);
// here you are
Of course you need two queries, one to select the columns and one to delete the null-rows. So the best way would be to know the columns in the first place.
Here's a snippet that should allow you to first SEE what your delete statements will look like, and then run it again when you're ready to actually perform the deletions:
begin
set nocount on
create table #temp_sql_text (sql_text varchar(max))
declare #table_name varchar(max)
declare c cursor for
select name from sys.tables order by name
open c
fetch next from c into #table_name
while ##fetch_status = 0
begin
insert into #temp_sql_text
select 'delete from ' + #table_name + ' where ' + replace(stuff(ColumnList, 1, 3, ''), '|||', ' is null and ') + ' is null'
from (
select '|||' + '[' + c.name + ']'
from sys.tables t inner join sys.columns c on c.object_id = t.object_id
where t.name = #table_name
order by c.column_id
for xml path('')
) o (ColumnList)
fetch next from c into #table_name
end
close c
deallocate c
declare #sql_text varchar(max)
declare c2 cursor for
select sql_text from #temp_sql_text
open c2
fetch next from c2 into #sql_text
while ##fetch_status = 0
begin
--Suggest just printing out the delete queries as a test before executing them!
print #sql_text
--Uncomment the next line to also execute the delete queries
--exec sp_executesql #sql_text
fetch next from c2 into #sql_text
end
close c2
deallocate c2
drop table #temp_sql_text
set nocount off
end
try this: I just tried on SQL server 2008 R2. I am not good with how to place the code in answer. please bare it.
declare #tempvar as nvarchar(max)
set #tempvar='X'
select #tempvar=#tempvar+COLUMN_NAME+' is NULL AND ' from
DatabaseName.INFORMATION_SCHEMA.COLUMNS where TABLE_NAME='Table_Name'
print #tempvar
Select #tempvar=CONCAT(#tempvar, 'X')
print #tempvar
Select #tempvar= REPLACE(#tempvar,'AND X','')
print #tempvar
Select #tempvar= REPLACE(#tempvar,'X',' ')
print #tempvar
declare #queryex as nvarchar(max)
set #queryex='select * from Table_Name where'+#tempvar
print #queryex
EXEC sp_executesql #queryex
If you know the number of columns in the table, you can do this:
delete from
tableToScrub
where
checksum(*) =
checksum(
cast(null as int),
cast(null as int)
-- Add one null to checksum per column
)
If you don't know the number of columns, you can do this:
with
nullset
as (
select
cast(null as int) as col1,
cast(null as int) as col2,
cast(null as int) as col3,
cast(null as int) as col4,
cast(null as int) as col5,
cast(null as int) as col6,
cast(null as int) as col7
-- add columns as necessary for your tables
)
,
nullchecksums
as (
select checksum(col1) as nullsum, 1 as columnCount from nullset union
select checksum(col1, col2), 2 from nullset union
select checksum(col1, col2, col3), 3 from nullset union
select checksum(col1, col2, col3, col4) , 4 from nullset union
select checksum(col1, col2, col3, col4, col5) , 5 from nullset union
select checksum(col1, col2, col3, col4, col5, col6) , 6 from nullset union
select checksum(col1, col2, col3, col4, col5, col6, col7) , 7 from nullset
-- add rows as necessary
)
delete from
tableToScrub
where
checksum(*) = (
select
nullsum
from
nullchecksums
where
columncount = (
select
count(1) as columnCount
from
sys.columns
where
object_id = object_id('SchemaName.TableToScrub')
)
)

COUNT the number of columns where a condition is true? SQL Server 2008 R2

I have a table that looks something like
ID Col1 Col2 Col3 Col4
1 3 5 3 3
What I want to do is COUNT the number of 3s in this particular row.
I have tried the
select COUNT(*)
from INFORMATION_SCHEMA.COLUMNS
where TABLE_NAME = 'TableName' -- but obviously I need WHERE Col1 = 3 OR Col2 = 3...
What would be the best way to achieve this?
Based on what OP asked, this can be done
select
CASE WHEN Col1 = 3 then 1 ELSE 0 END +
CASE WHEN Col2 = 3 then 1 ELSE 0 END +
CASE WHEN Col3 = 3 then 1 ELSE 0 END +
CASE WHEN Col4 = 3 then 1 ELSE 0 END
From TableName
I don't really enjoy working with PIVOT so here a solution using APPLY.
SELECT
T.id
, Val
, COUNT(*)
FROM MyTable AS T
CROSS APPLY (
VALUES
(T.C1)
, (T.C2)
, (T.C3)
, (T.C4)
) AS X(Val)
GROUP BY T.Id, X.Val
ORDER BY T.Id, X.val
Please find the sample code:
DECLARE #Query VARCHAR(MAX) = 'SELECT Count = '
SELECT
#Query += '( CASE WHEN '+ COLUMN_NAME + ' = 3 THEN 1 ELSE 0 END ) + '
FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = 'TEST' AND COLUMN_NAME <> 'ID'
SET #Query = SUBSTRING(#Query, 1, DATALENGTH(#Query) - 2) + ' FROM TEST WHERE ID = 1'
EXEC(#Query)

Transposing Rows in to colums in SQL Server 2005

I have an sql query "Select * from tablename" whose output is
col1 col2
A 1
B 2
C 3
I want to modify the above output to below as following
A B C
1 2 3
Please let me know how could I achieve this
You will need to perform a PIVOT. There are two ways to do this with PIVOT, either a Static Pivot where you code the columns to transform or a Dynamic Pivot which determines the columns at execution.
Static Pivot:
SELECT *
FROM
(
SELECT col1, col2
FROM yourTable
) x
PIVOT
(
min(col2)
for col1 in ([A], [B], [C])
)p
See SQL Fiddle with Demo
Dynamic Pivot:
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
select #cols = STUFF((SELECT distinct ',' + QUOTENAME(col1)
from t1
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = 'SELECT ' + #cols + ' from
(
select col1, col2
from t1
) x
pivot
(
min(col2)
for col1 in (' + #cols + ')
) p '
execute(#query)
See SQL Fiddle with Demo
If you do not want to use the PIVOT function, then you can perform a similar type of query with CASE statements:
select
SUM(CASE WHEN col1 = 'A' THEN col2 END) as A,
SUM(CASE WHEN col1 = 'B' THEN col2 END) as B,
SUM(CASE WHEN col1 = 'C' THEN col2 END) as C
FROM t1
See SQL Fiddle with Demo
You want to use PIVOT or COALESCE in sql.Here is a nice examples how to converting rows to columns.
Five methods converting rows to columns
.

SQL: count number of distinct values in every column

I need a query that will return a table where each column is the count of distinct values in the columns of another table.
I know how to count the distinct values in one column:
select count(distinct columnA) from table1;
I suppose that I could just make this a really long select clause:
select count(distinct columnA), count(distinct columnB), ... from table1;
but that isn't very elegant and it's hardcoded. I'd prefer something more flexible.
This code should give you all the columns in 'table1' with the respective distinct value count for each one as data.
DECLARE #TableName VarChar (Max) = 'table1'
DECLARE #SqlString VarChar (Max)
set #SqlString = (
SELECT DISTINCT
'SELECT ' +
RIGHT (ColumnList, LEN (ColumnList)-1) +
' FROM ' + Table_Name
FROM INFORMATION_SCHEMA.COLUMNS COL1
CROSS AppLy (
SELECT ', COUNT (DISTINCT [' + COLUMN_NAME + ']) AS ' + '''' + COLUMN_NAME + ''''
FROM INFORMATION_SCHEMA.COLUMNS COL2
WHERE COL1.TABLE_NAME = COL2.TABLE_NAME
FOR XML PATH ('')
) TableColumns (ColumnList)
WHERE
1=1 AND
COL1.TABLE_NAME = #TableName
)
EXECUTE (#SqlString)
try this (sql server 2005 syntax):
DECLARE #YourTable table (col1 varchar(5)
,col2 int
,col3 datetime
,col4 char(3)
)
insert into #YourTable values ('abcdf',123,'1/1/2009','aaa')
insert into #YourTable values ('aaaaa',456,'1/2/2009','bbb')
insert into #YourTable values ('bbbbb',789,'1/3/2009','aaa')
insert into #YourTable values ('ccccc',789,'1/4/2009','bbb')
insert into #YourTable values ('aaaaa',789,'1/5/2009','aaa')
insert into #YourTable values ('abcdf',789,'1/6/2009','aaa')
;with RankedYourTable AS
(
SELECT
ROW_NUMBER() OVER(PARTITION by col1 order by col1) AS col1Rank
,ROW_NUMBER() OVER(PARTITION by col2 order by col2) AS col2Rank
,ROW_NUMBER() OVER(PARTITION by col3 order by col3) AS col3Rank
,ROW_NUMBER() OVER(PARTITION by col4 order by col4) AS col4Rank
FROM #YourTable
)
SELECT
SUM(CASE WHEN col1Rank=1 THEN 1 ELSE 0 END) AS col1DistinctCount
,SUM(CASE WHEN col2Rank=1 THEN 1 ELSE 0 END) AS col2DistinctCount
,SUM(CASE WHEN col3Rank=1 THEN 1 ELSE 0 END) AS col3DistinctCount
,SUM(CASE WHEN col4Rank=1 THEN 1 ELSE 0 END) AS col4DistinctCount
FROM RankedYourTable
OUTPUT:
col1DistinctCount col2DistinctCount col3DistinctCount col4DistinctCount
----------------- ----------------- ----------------- -----------------
4 3 6 2
(1 row(s) affected)
and it's hardcoded.
It is not hardcoding to provide a field list for a sql statement. It's common and acceptable practice.
This won't necessarily be possible for every field in a table. For example, you can't do a DISTINCT against a SQL Server ntext or image field unless you cast them to other data types and lose some precision.
I appreciate all of the responses. I think the solution that will work best for me in this situation (counting the number of distinct values in each column of a table from an external program that has no knowledge of the table except its name) is as follows:
Run "describe table1" and pull out the column names from the result.
Loop through the column names and create the query to count the distinct values in each column. The query will look something like "select count(distinct columnA), count(distinct columnB), ... from table1".
Raj More's answer works well if you don't need to consider null as a value as count(distinct...) does not count null.
Here is a modification to count values including null by converting values to a string and replacing null with "NULL AS SOME IMPOSSIBLE STRING":
DECLARE #TableName VarChar (1024) = 'tableName'
DECLARE #SqlString VarChar (Max)
set #SqlString = (
SELECT DISTINCT
'SELECT ' +
RIGHT (ColumnList, LEN (ColumnList)-1) +
' FROM ' + Table_Name
FROM INFORMATION_SCHEMA.COLUMNS COL1
CROSS AppLy (
SELECT ', COUNT (DISTINCT coalesce(cast([' + COLUMN_NAME + '] as varchar),
''NULL AS SOME IMPOSSIBLE STRING'')) AS ' + '''' + COLUMN_NAME + ''''
FROM INFORMATION_SCHEMA.COLUMNS COL2
WHERE COL1.TABLE_NAME = COL2.TABLE_NAME
FOR XML PATH ('')
) TableColumns (ColumnList)
WHERE
COL1.TABLE_NAME = #TableName
)
EXECUTE (#SqlString)
DISTINCT is evil. Do COUNT/GROUP BY