View without null columns - sql

Is there a way to create a view of a table without the columns that are null.
What i mean by that is that i want a view where there are only columns where at least one row has some data in that column and is not empty.
In this example, the result would be a view with columns A and C
I'm using Microsoft SQL and the data in db aren't going to be updated.

That is not possible via the view, because the view cannot handle the dynamic column set. It is possible only via the stored procedure returning the result set. I made not a very beautiful and not generic decision, but I think you can upgrade it if you can.
First, we need to create a function which returns the set of columns which have at least one non-null value:
create function getTheColumnList() returns nvarchar(max) as
begin
declare #columnlist nvarchar(max);
set #columnlist = ''
declare #totalrows int;
set #totalrows = (select
count(*)
from table_name)
if (select
count(*)
from table_name
where A is null) < #totalrows
set #columnlist = #columnlist + 'A, '
if (select
count(*)
from table_name
where B is null) < #totalrows
set #columnlist = #columnlist + 'B, '
if (select
count(*)
from table_name
where C is null) < #totalrows
set #columnlist = #columnlist + 'C, '
set #columnlist = replace(#columnlist + '%', ', %', '')
return #columnlist;
end
go
Then we need to use this function in a dynamic SQL to construct the needed query. It can be done inside the stored procedure:
create proc getNottNullFromTableName as
declare #SQL nvarchar(1000)
declare #columnist varchar(50)
set #columnist = dbo.getTheColumnList();
set #SQL = 'SELECT ' + #columnist + ' FROM table_name';
exec (#SQL)
go
Finally, invoke that procedure:

Related

Verify all columns can convert from varchar to float

I have tried a bunch of different ways like using cursors and dynamic SQL, but is there a fast way to verify that all columns in a given table can convert from varchar to float (without altering the table)?
I want to get a print out of which columns fail and which columns pass.
I am trying this method now but it is slow and cannot get the list of columns that pass or error out.
drop table users;
select *
into users_1
from users
declare #cols table (i int identity, colname varchar(100))
insert into #cols
select column_name
from information_schema.COLUMNS
where TABLE_NAME = 'users'
and COLUMN_NAME not in ('ID')
declare #i int, #maxi int
select #i = 1, #maxi = MAX(i) from #cols
declare #sql nvarchar(max)
while(#i <= #maxi)
begin
select #sql = 'alter table users_1 alter column ' + colname + ' float NULL'
from #cols
where i = #i
exec sp_executesql #sql
select #i = #i + 1
end
I found this code on one of the SQL tutorials sites.
Why all the drop/create/alter nonsense? If you just want to know if a column could be altered, why leave your table in a wacky state, where the columns that can be altered are altered, and the ones that can't just raise errors?
Here's one way to accomplish this with dynamic SQL (and with some protections):
DECLARE #tablename nvarchar(513) = N'dbo.YourTableName';
IF OBJECT_ID(#tablename) IS NOT NULL
BEGIN
DECLARE #sql nvarchar(max) = N'SELECT ',
#tmpl nvarchar(max) = N'[Can $colP$ be converted?]
= CASE WHEN EXISTS
(
SELECT 1 FROM ' + #tablename + N'
WHERE TRY_CONVERT(float, COALESCE($colQ$,N''0'')) IS NULL
)
THEN ''No, $colP$ cannot be coverted''
ELSE ''Yes, $colP$ CAN be converted'' END';
SELECT #sql += STRING_AGG(
REPLACE(REPLACE(#tmpl, N'$colQ$',
QUOTENAME(name)), N'$colP$', name), N',')
FROM sys.columns
WHERE object_id = OBJECT_ID(#tablename)
AND name <> N'ID';
EXEC sys.sp_executesql #sql;
END
Working db<>fiddle
This is never going to be "fast" - there is no great shortcut to having to read and validate every value in the table.

Information_schema.columns in the query

I need to pull the data from custom columns in all tables which have "custom1, custom2, custom3....." columns.
Declare #TableName varchar(max)
set #TableName = 'RandomTable';
with main as
(
select distinct
infos.COLUMN_NAME, infos.TABLE_NAME
from
INFORMATION_SCHEMA.COLUMNS infos
where
infos.TABLE_NAME = #TableName
and infos.COLUMN_NAME like 'Custom%%'
)
This query returns the list of custom columns in any table I specify in the parameter. Any idea how to use that in the query so I can get all the data from the RandomTable.Custom%% columns?
Any idea? I'm loosing the plot on it.
Following incomudro idea and guessing you are using SQL Server you could do something like this:
DECLARE
#TableName VARCHAR(MAX)
,#ColumnName VARCHAR(MAX)
,#SQLQuery VARCHAR(MAX)
,#FirstFlag BIT
SET #TableName = 'TEST'
SELECT
COL.COLUMN_NAME
INTO ##CUSTOM_COL
FROM INFORMATION_SCHEMA.COLUMNS COL
WHERE 1 = 1
AND COLUMN_NAME LIKE 'CUSTOM__'
AND TABLE_NAME = #TableName
DECLARE CUR_CUSTOM_COL CURSOR FOR
SELECT * FROM ##CUSTOM_COL
OPEN CUR_CUSTOM_COL
FETCH NEXT FROM CUR_CUSTOM_COL INTO #ColumnName
SET #FirstFlag = 1
SET #SQLQuery = 'SELECT *'
WHILE ##FETCH_STATUS = 0
BEGIN
IF #FirstFlag = 0
BEGIN
SET #SQLQuery = #SQLQuery + ', '
END
SET #SQLQuery = #SQLQuery + #ColumnName
SET #FirstFlag = 0
FETCH NEXT FROM CUR_CUSTOM_COL INTO #ColumnName
END
CLOSE CUR_CUSTOM_COL
DEALLOCATE CUR_CUSTOM_COL
SET #SQLQuery = #SQLQuery + ' FROM ' + #TableName
EXEC(#SQLQuery)
I saved my workspace in this fiddle. Unfortunaly it doesnt work there as intendet, but it should in your SQL Client(or I did a little typing mistake). With some little modifications you could not only display the custom columns from 1 specified table but from all tables. Also you could, besides selecting it, insert the ouput from the custom columns in temp tables.

T-SQL Setting a scalar variable with the value of another scalar variable

Im creating a stored procedure that retrieves data to fill a radar chart. It worked pretty well using static tables und rows like this:
(This is just a piece of the code)
SELECT
#aAvg = CAST(AVG(1. * foerderpy_1617) as DECIMAL(18,4)),
#aMin = CAST(MIN(1. * foerderpy_1617) as DECIMAL(18,4)),
#aMax = CAST(MAX(1. * foerderpy_1617) as DECIMAL(18,4))
FROM foerderpy a WHERE SUBSTRING(a.BSN,3,1) = 'g';
But now i want a dynamic sql. I want the stored procedure to always take the latest row of my table:
(These are just pieces of the code)
DECLARE #SQL AS NVARCHAR(MAX);
DECLARE #aAvg AS NVARCHAR(MAX);
DECLARE #aMin AS NVARCHAR(MAX);
DECLARE #aMax AS NVARCHAR(MAX);
DECLARE #tabname SYSNAME;
DECLARE #coluname SYSNAME;
DECLARE #counter INTEGER;
SET #tabname = 'foerderpy'
SET #counter = (
SELECT MAX(ORDINAL_POSITION)
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = #tabname
GROUP BY TABLE_NAME)
SET #coluname = (
SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = #tabname AND
ORDINAL_POSITION = #counter)
SET #aAvg = (SELECT CAST(AVG(1. * #coluname) as DECIMAL(18,4))FROM #tabname a WHERE SUBSTRING(a.BSN,3,1) = SUBSTRING(#restriction,3,1))
At the last line (the SET #aAvg), the stored procedure stops working and sql tells me "i have to declare #tabname", although i obv. declared it above. What is the problem im missing? Is it even possible to do what im trying?
The rest of the Code isn't causing any problems so i left it out. I need the #aAvg to calculate later in the procedure.
You need to run your last query using EXECUTE because EXECUTE:
Executes a command string or character string within a Transact-SQL batch
So you have to change the last line of your procedure in a way that the query is written in a string and called by execute.
DECLARE #sql VARCHAR(max) = 'SELECT CAST(AVG(1. * ' + #coluname + ') as DECIMAL(18,4))FROM '+ #tabname +' a WHERE SUBSTRING(a.BSN,3,1) = SUBSTRING('+#restriction+',3,1)';
EXECUTE(#sql);
If you would like to save the value in your variable #aAvg, you can use sp_executesql with an out parameter, this way:
DECLARE #sql VARCHAR(max) = 'SELECT CAST(AVG(1. * ' + #coluname + ') as DECIMAL(18,4))FROM '+ #tabname +' a WHERE SUBSTRING(a.BSN,3,1) = SUBSTRING('+#restriction+',3,1)';
exec sp_executesql #sql, N'#aAvg decimal(18,4) out', #aAvg out
select #aAvg

Looping through a column in SQL table that contains names of other tables

I have fairly new to using SQL, currently I have a table that has a column that contains the names of all the tables I want to use for one query, so what I want to do is to loop through that column and go to every single one of these tables and then search one of their columns for a value (there could be multiple values), so whenever a table contains the value, I will list the name of the table. Could someone give me a hint of how this is done? Is cursor needed for this?
I don't have enough reputation to comment but is the table with the column that contain the table names all in one column, meaning that all the table names are comma separated or marked with some sort of separator? This would cause the query to be a little more complicated as you would have to take care of that before you start looping through your table.
However, this would require a cursor, as well as some dynamic sql.
I will give a basic example of how you can go about this.
declare #value varchar(50)
declare #tableName varchar(50)
declare #sqlstring nvarchar(100)
set #value = 'whateveryouwant'
declare #getTableName = cursor for
select tableName from TablewithTableNames
OPEN #getTableName
fetch NEXT
from #getTableName into #tableName
while ##FETCH_STATUS = 0
BEGIN
set #sqlstring = 'Select Count(*) from ' + #tableName + 'where ColumnNameYouwant = ' + #value
exec #sqlstring
If ##ROWcount > 0
insert into #temptable values (#tableName)
fetch next
from #getTableName into #tableName
END
select * from #temptable
drop table #temptable
close #getTableName
deallocate #getTableName
I'm currently not able to test this out as for time constraint reasons, but this is how I would go about doing this.
You could try something like this:
--Generate dynamic SQL
DECLARE #TablesToSearch TABLE (
TableName VARCHAR(50));
INSERT INTO #TablesToSearch VALUES ('invoiceTbl');
DECLARE #SQL TABLE (
RowNum INT,
SQLText VARCHAR(500));
INSERT INTO
#SQL
SELECT
ROW_NUMBER() OVER (ORDER BY ts.TableName) AS RowNum,
'SELECT * FROM ' + ts.TableName + ' WHERE ' + c.name + ' = 1;'
FROM
#TablesToSearch ts
INNER JOIN sys.tables t ON t.name = ts.TableName
INNER JOIN sys.columns c ON c.object_id = t.object_id;
--Now run the queries
DECLARE #Count INT;
SELECT #Count = COUNT(*) FROM #SQL;
WHILE #Count > 0
BEGIN
DECLARE #RowNum INT;
DECLARE #SQLText VARCHAR(500);
SELECT TOP 1 #RowNum = RowNum, #SQLText = SQLText FROM #SQL;
EXEC (#SQLText);
DELETE FROM #SQL WHERE RowNum = #RowNum;
SELECT #Count = COUNT(*) FROM #SQL;
END;
You would need to change the "1" I am using as an example to the value you are looking for and probably add a CONVERT/ CAST to make sure the column is the right data type?
You actually said that you wanted the name of the table, so you would need to change the SQL to:
'SELECT ''' + ts.TableName + ''' FROM ' + ts.TableName + ' WHERE ' + c.name + ' = 1;'
Another thought, it would probably be best to insert the results from this into a temporary table so you can dump out the results in one go at the end?

Get column names in SQL server that satisfy a where condition on data

I just had a random doubt while working with SQL-server to which i thought i could get it clarified here.
Say- i have a condition that i want to find out all the column names in the database which satisfy my where condition on data.
Example:-
There are some 20-30 tables in a SQL-Server DB.Now all i need is a query to find out the list of column names which have "Ritesh" as a data field in them.
I don't know if it is really possible in the first place.
I hope i am clear. Please, any help will be most appreciated.
Thank You.
Ritesh.
This should work, but be aware, this will take a while to execute in large databases. I have assumed that the search string might be just a part of the data contained and am using wildcards. I feel this is purely academic, as I am unable to imagine a scenario, where this will be required.
--you need to iterate the whole columns of entire table to find the matched record
--another thing is that you need dynamic sql to find the table name
DECLARE #Value varchar(50) --value for that find the column Name
SET #Value = 'Ritesh'
CREATE TABLE #Table
(
TableName Varchar(500),ColumnName Varchar(500),
Id int Identity(1,1) --use for iteration
)
CREATE TABLE #Results
(
TableName varchar(500),
ColumnName varchar(500)
)
INSERT INTO #Table
SELECT
TABLE_SCHEMA + '.' + TABLE_NAME AS TableNam,
Column_name AS ColumnName
FROM INFORMATION_SCHEMA.COLUMNS
WHERE Data_type IN ('char', 'nchar', 'varchar', 'nvarchar')
--change the datatype based on the datatype of sample data you provide
-- also remember to change the wildcard, if the input datatype is not a string
DECLARE #Count Int --total record to iterated
SET #Count = 0;
SELECT
#Count = COUNT(*)
FROM #Table
DECLARE #I int --initial value one to iterate
SET #I = 1;
DECLARE #TableName varchar(500)
SET #TableName = ''
DECLARE #ColumnName varchar(500)
SET #ColumnName = ''
DECLARE #Str nvarchar(1000)
SET #Str = ''
DECLARE #param nvarchar(1000)
SET #param = ''
DECLARE #TableNameFound varchar(max)
SET #TableNameFound = ''
DECLARE #Found bit
SET #Found = 0;
WHILE #I<=#Count
BEGIN
SET #Found = 0;
SELECT
#TableName = TableName,
#ColumnName = ColumnName
FROM #Table
WHERE Id = #I;
SET #param = '#TableName varchar(500),#ColumnName varchar(500),#Value varchar(50),#TableNameFound varchar(max),#Found bit output'
SET #str = 'Select #Found=1 From ' + #TableName + ' where ' + #ColumnName + ' Like ' + '''' + '%' + #Value + '%' + ''''
-- here we are using tablename and actual value to find in table
EXEC sp_executesql #str,
#param,
#TableName,
#ColumnName,
#Value,
#TableNameFound,
#Found OUTPUT
IF #Found=1
BEGIN
INSERT INTO #Results (TableName, ColumnName)
SELECT
#TableName,
#ColumnName
END
--increment value of #I
SET #I = #I + 1;
END
--Display Results
SELECT * FROM #Results
--Clean Up
DROP TABLE #Table
DROP TABLE #Results