Is there a quick way to check if ANY column is NULL? - sql

I have a table with around 20 columns. Aside from typing out:
Where column1 is null OR column2 is null OR column3 is null etc...
Is there a quicker way to just check every column and see if any value is null and if so, return that record?

No. There are ways to code it quicker, but there are no shortcuts like you imply. Taken from an answer I gave on dba.stackexchange:
DECLARE #tb NVARCHAR(255), #sql NVARCHAR(MAX);
SET #tb = N'dbo.[table]';
SET #sql = N'SELECT * FROM ' + #tb + ' WHERE 1 = 0';
SELECT #sql = #sql + N' OR ' + QUOTENAME(name) + ' IS NULL'
FROM sys.columns
WHERE [object_id] = OBJECT_ID(#tb);
EXEC sp_executesql #sql;

You can find the column names using something like this:
SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.Columns where TABLE_NAME = <table_name>
Then, I would write a procedure using this, and that would loop through the entries in your table and the column names.
Source: http://codesnippets.joyent.com/posts/show/337

That depends on what quicker means.
If you mean quicker for SQL Server to execute, one thing you could do is write a trigger than updates a bit column that specifies if the entire row (other than the bit and primary key) are NULL. But, there should be a REAL good reason for this as it will impact your update performance. Indexes on those columns would help as well.
If you mean quicker to write, you could have SQL generate the where clause for you. But, unless you do this alot, it isn't worth the time in my opinion.

Teaching-to-fish-instead-of-giving-you-the-fish kind of answer here:
One way of doing it is by creating a Stored Procedure that assembles and runs a dynamic query.
The Stored procedure will:
have a Table name as input parameter.
query the meta data system tables for the specific table structure.
dynamically assemble a string (the actual query) with the OR statements for that table's columns.
run the assembled query and return the result.

Related

Should I always use dynamic sql when programatically use some stored procedure?

I have a stored procedure that can get the number of records in a table, in which the #tableName is the parameter of the stored procedure. Let's call it FastCount:
SELECT OBJECT_NAME(object_id), SUM(row_count) AS rows
FROM sys.dm_db_partition_stats
WHERE object_id = OBJECT_ID(#tableName)
AND index_id < 2
GROUP BY OBJECT_NAME(object_id);
Now, let's say I have 50 tables, like data_1950, data_1951, .....data_2000. I wrote a batch, query each table's records count, and put them into a temporary table. It works like a charm
CREATE TABLE #Temp
(
TableName varchar(30),
RecordsCount int
)
DECLARE #sql as varchar(max)
DECLARE #yearN as int = 1950
DECLARE #tbName as sysname
WHILE #yearN <= 2000
BEGIN
SET #tbName = QUOTEName(N'[dbo].data_' + Convert(varchar,#yearN))
SET #sql = N'Exec [dbo].FastCount #tableName=' + #tbName
INSERT INTO #Temp
EXEC (#sql)
SET #yearN = #yearN + 1
END
SELECT * FROM #Temp
DROP TABLE #Temp
However, if I replace the dynamic SQL string part
SET #sql = N'Exec [dbo].FastCount #tableName=' + #tbName
INSERT INTO #Temp
EXEC (#sql)
with a straightforward call
INSERT INTO #Temp
EXEC [dbo].FastCount #tableName = #tbName
Then the whole batch just not work...
So I don't understand why... Should I always use dynamic SQL string and exec(#sql) when programmatically using the stored procedure. A big thanks for taking the time to look.
OK, here is what is happening in the two scenarios that you posed in your original question. (Yes, the reality is that there are probably better ways to achieve your end result, but let's look at the actual problem that you posed .... why is the behaviour of your INSERT / EXEC different, depending on how you made the call).
First, you have your variable declared, that will contain your table name:
DECLARE #tbName as sysname
Then you have your looping block that incrementally increases the year number, to generate the different table names. There's nothing inherently wrong with the looping block, so let's just look at an example using one of the table names, to see what's happening within the WHILE block. Take the first table name as the example, which would be [dbo].data_1950.
Your statement:
set #tbName = QUOTEName(N'[dbo].data_' + Convert(varchar,#yearN))
ultimately takes the string "[dbo].data_1950" - which comes from concatenating '[dbo].data_' with the year number (in this case, 1950) converted to a string (varchar) - and passes it to the QUOTENAME() function. The QUOTENAME() function takes its input and a second parameter, which is the character that the input should be quoted with (if the 2nd parameter is not passed, then the default is []). Thus, if we then converted the #tbName variable to a string, it would appear like this:
[[dbo].data_1950]
Now we get to see the funky way that SQL deals with "sysname" data-types. (In fact, as you read further down, maybe the issue is not primarily tied to the "sysname" data-type, but anyhow, take away from this what you will). To be honest, "sysname" is, in itself, a little bit of a funky data-type anyway, which I tend to steer away from, unless absolutely necessary. But anyhow, on to the details of the issue that you were seeing.
Step 1 - I created a version of your stored proc, but I included a statement that would output the value of the #tableName parameter that was passed in. This gives us an opportunity to see what SQL is doing in the two different scenarios, and then explain why the results are different.
CREATE PROC [dbo].FastCount
(
#tableName varchar(100)
)
AS
BEGIN
PRINT #tableName;
SELECT OBJECT_NAME(object_id), SUM(row_count) AS rows
FROM sys.dm_db_partition_stats
WHERE object_id = OBJECT_ID(#tableName)
AND index_id < 2
GROUP BY OBJECT_NAME(object_id);
END
Step 2 - our first scenario is executing the dynamic SQL.
set #tbName = QUOTEName(N'[dbo].data_' + Convert(varchar,#yearN))
set #sql = N'Exec [dbo].FastCount #tableName=' + #tbName
Insert Into #Temp Exec(#sql)
Now, we know that the #tbName variable contains
[[dbo].data_1950]
and therefore we can then infer that the #sql variable contains
Exec [dbo].FastCount #tableName=[[dbo].data_1950]
so that is effectively the statement that is executed by the Exec(#sql) command.
When this runs, and we look at the output of the PRINT command, we see
[dbo].data_1950
and we see a result from our query (the table name and row count). This makes sense, of course, because our table name is "data_1950", and the schema of the table is "dbo", so the SELECT statement to get the row count is going to work as expected.
Step 3 - run the EXEC command directly, without the use of the #sql variable, ie.
Insert Into #Temp Exec [dbo].FastCount #tableName = #tbName
Now, when we look at the output of the PRINT command for this execution of the "FastCount" stored procedure, we see
[[dbo].data_1950]
Of course, this is now NOT going to produce the results that we expect, because we're telling SQL to find the row count for a table named "[dbo].data_1950" (in the absence of the specific schema, SQL will just assume the default schema. In this case, with a schema of [dbo], we'd be telling SQL to get the row count from a table named [dbo].[[dbo].data_1950] - which is clearly NOT the table name).
You should see the obvious difference - in one scenario, the parameter value that is passed into the stored is the "correct" reference to the table name, and in the other scenario it is not.
As a final step, let's look at how the "non-dynamic" SQL would be executed, to achieve the results that we need. In this instance, there's no need for the QUOTENAME() function:
set #tbName = N'[dbo].data_' + Convert(nvarchar,#yearN)
Insert Into #Temp Exec [dbo].FastCount #tableName = #tbName
When we run it in this way, we see the expected output ([dbo].data_1950) from the PRINT command, and we see the expected query results (containing the table name and row count).
Can I explain this behaviour, exactly? Errr, not necessarily ... maybe someone else will be able to explain specifically what is happening, and why. My only interpretation is that when the EXEC() statement is passed the dynamic sql (ie. #sql variable) it is first interpreting the entire string and stripping out identifiers (in the case, the surrounding [] ... on what basis is it making that decision, I don't know). As opposed to the non-dynamic execution, where the #tbName value ([[dbo].data_1950]) is just being passed straight in as the parameter, with no modification (and thus causing the unexpected end result that we saw).
Hopefully this information is useful to you (or, at least, to someone else in the future!).
In general you should avoid dynamic SQL, and you should avoid granting rights to execute dynamic SQL, unless absolutely necessary. This is for performance and security reasons.
The typical way to deal with this situation is to use a partitioned view:
CREATE VIEW DataView
AS
SELECT '1950' TableName, * FROM Data_1950
UNION ALL
SELECT '1951' TableName, * FROM Data_1951
UNION ALL
SELECT '1952' TableName, * FROM Data_1952
UNION ALL
SELECT '1953' TableName, * FROM Data_1953
UNION ALL
SELECT '1954' TableName, * FROM Data_1954
UNION ALL
SELECT '1955' TableName, * FROM Data_1955
(Keep adding select statements until you have covered all of your tables.)
Now to get your table counts all you need to do is execute this:
SELECT TableName, COUNT(*) RecordCount
FROM DataView
GROUP BY TableName
Isn't that much easier?

Perform query on multiple tables

I need to apply the following query to 60+ tables:
UPDATE variable_table_name ab
SET elem_nr = ef.elem_nr
FROM ga_mit_elemnr ef
WHERE (ab.elem_nr NOT IN
(SELECT elem_nr
FROM strassenelemente cd)
OR ab.elem_nr IS NULL)
AND St_within(ab.geom, ef.geom)
Is there a way to automate this in PostgreSQL?
As far as I am aware it is not possible to do this in Plain SQL, however you could use PL/pgSQL to execute the query as dynamic command in a for loop and run this query against your 60+ tables. You can read about Pl/pgSQL here: https://www.postgresql.org/docs/9.6/plpgsql.html
Depending on your requirements you could also use other programming languages such as Python to dynamically execute SQL-statements as string.
After understanding the case, I would suggest using postgres partitioning mechanism
I.e have a master table and 60+ partitions, each partition will be attached to the master, and on ALTER TABLE you alter only the master table schema, and all the rest will be updated as well
Something like this..?
DECLARE #sql NVARCHAR(max)
SELECT #sql = N' '+ String_agg(Concat(N'
UPDATE ', CONVERT(NVARCHAR(max), Quotename(col_table_name)), N'
SET column1 = value1, column2 = value2...., columnN = valueN
WHERE [condition];'), N'' + CHAR(13) + CHAR(13))
FROM information_schema.tables
WHERE [condition]
EXEC (#sql)
Once you get the query, you can check the query by using the SELECT statement.
SELECT (#sql)
If the query is okay, then you can execute it.
EXEC (#sql)
Example: https://data.stackexchange.com/stackoverflow/query/1011376/

SSMS - MS SQL Sever Query option set ON/OFF to display all columns in Shortdate format?

In SSMS, for MS SQL Server 2008 or newer versions, is there a general query option or something like that, something to set ON or OFF before launching the query, in order to view all DATE columns as Shortdate (only date, without time)?
Something like SET ANSI_NULLS { ON | OFF } ?
Because I often use 'select * from table', or different approaches like that, and inside tables are many columns and the DATE columns are in different places, and I don't want every time to check where these columns are and to explicitly use CONVERT or CAST only on them, to display them properly.
Thank you for any suggestion.
Yeah I will solve such situation from interface end only.
Also saying like,
Because I often use 'select * from table', or different approaches
this is itself bad,you can't have your own way or approaches.
Nonetheless in sql we can do something like this,
USE AdventureWorks2012
GO
--proc parameter
DECLARE #tablename VARCHAR(50) = 'Employee'
DECLARE #table_schema VARCHAR(50) = 'HumanResources'
--local variable
DECLARE #Columnname VARCHAR(max) = ''
DECLARE #Sql VARCHAR(max) = ''
SELECT #Columnname = #Columnname + CASE
WHEN DATA_TYPE = 'date'
OR DATA_TYPE = 'datetime'
THEN 'cast(' + QUOTENAME(COLUMN_NAME) + ' as date)'
ELSE QUOTENAME(COLUMN_NAME)
END + ',' + CASE
WHEN DATA_TYPE = 'date'
OR DATA_TYPE = 'datetime'
THEN 'cast(' + QUOTENAME(COLUMN_NAME) + ' as date)'
ELSE QUOTENAME(COLUMN_NAME)
END + ','
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = #tablename
AND TABLE_SCHEMA = #table_schema
ORDER BY ORDINAL_POSITION
SET #Columnname = STUFF(#Columnname, len(#Columnname), 1, '')
--set #Columnname=stuff(#Columnname,1,1,'')
--PRINT #Columnname
SET #Sql = 'select ' + #Columnname + ' from ' + #table_schema + '.' + #tablename + ''
--PRINT #Sql
EXEC (#Sql)
it can be further improve as per requirement.Also please use sp_executeSql
you can customize case condition.
There is no "magic" display format button or function in SSMS no. When you execute a query, SSMS will display that column in the format that is appropriate for that data type; for a datetime field that will include the time.
If you don't want to include the time, then you have to either CAST or CONVERT the individual column(s), or format the data appropriately in your presentation layer (for example, if you're using Excel then dd/MM/yyyy may be appropriate).
If all your columns have '00:00:00.000' at the end of their value, the problem isn't the display format, it's your data type choice. Clearly, the problem isn't that SSMS is returning a time for a date**time** column, it's that you've declare a column as a datetime when it should have been a date. You can change the datatype of a column using ALTER. For example:
USE Sandbox;
Go
CREATE TABLE TestTable (ID smallint IDENTITY(1,1), DateColumn datetime);
INSERT INTO TestTable (DateColumn)
VALUES ('20180201'),('20180202'),('20180203'),('20180204'),('20180205');
SELECT *
FROM TestTable;
GO
ALTER TABLE TestTable ALTER COLUMN DateColumn date;
GO
SELECT *
FROM TestTable;
GO
DROP TABLE TestTable;
TL;DR: SSMS displays data in an appropriate format for the data you have. If you don't like it, you have to supply an alternate format for it to display for each appropriate column. If the issue is your data, change the data type.
Edit: I wanted to add a little more to this.
This question is very much akin to also asking "I would like to be able to run queries where decimals only return the integer part of the value. Can this be done automagically?". So, the value 9.1 would return 9, but also, the value 9.999999999 would return 9.
Now, I realise that you "might" be thinking "Numbers aren't anything like dates", but really, they are. At the end of the (especially in data) a date is just a number (hell, a datetime time in SQL Server is stored as the number of days after 1900-01-01, and the time is a decimal of that number, so 43136.75 is actually 2018-02-07 18:00:00.000).
Now that we're talking in numbers, does it seems like a good idea to you to have all your decimals returned as their FLOOR value? I imagine the answer is "no". Imagine if you were doing some kind of accounting, and only summing the values of transactions using the FLOOR value. You could be losing 1,000's (or more) of £/$/€'s.
Think of the old example of the people who stole money from payments which contained values of less than a penny. The amount they stole was a huge amount, however, not one individual theft had a value >= $0.01. The same principle really rules here; precision is very important and if your column has that precision it should be there for a reason.
The same is true for dates. If you are storing times with dates, and the time isn't relevant for that specific query, change your query; having a setting to ignore times (or decimal points) is, in all honestly, just a bad idea.
I don't think that there is an option like this in SSMS. The best thing I am coming up with is to create views of the tables and this way you can do a
select convert(date, <date column>)
one time and they will appear as just dates in the views.

How I can use a single stored procedure for data insertion in multiple tables in SQL Server 2008?

Suppose I have many tables in my database. Every time I will insert data in any table. So, can I use a single stored procedure for all my data insertion in every table, instead of writing one stored procedure for each table?
In this scenario, each time I will pass table name and parameters dynamically to the stored procedure. If yes, can anyone give some basic idea how to perform this? If any extra information is required, please ask.
Thanks and regards,
Rizwan Gazi.
You could work with dynamic SQL and build the insert statement on the fly. THIS IS NOT RECOMMENDED but it should solve the problem you're asking about.
(I haven't run this code, but you can see what is being accomplished here with building the insert string and then executing it)
In this procedure, you pass in the table name, columns and values you care about and fire it off in a row based operation. With some minor tweaks you would be able to make it set based as well.
create procedure dbo.TableInsert
#tablename varchar(100)
, #columnlist varchar(max)
, #valueslist varchar(max)
as
begin
declare #sql varchar(max)
set #sql =
'insert into ' + #tablename
+ '(' + #columnlist + ')'
+ ' VALUES (' + #valueslist + ')'
print(#sql)
sp_executesql (#sql)
end
go
Execution would look something like this:
exec dbo.TableInsert
#tablename = 'TestTable'
, #columnlist = 'col1, col2, col3'
, #valuelist = '1,2,3'
Text insert would be a little trickier in this version since you have to wrap it around in single quotes.
exec dbo.TableInsert
#tablename = 'TestTable'
, #columnlist = 'col1, col2, col3'
, #valuelist = '''1'',''2'',''3'''
You could do something using dynamic SQL to build a query and then run it using:
EXEC SP_EXECUTESQL(#SQL)
(Assuming MS SQL Server)
Not sure I'd recommend it though and will probably be a total nightmare to test and maintain. Having different sprocs would be easier to test and maintain going forward and would perform better as the different sprocs would have separate query plans.
If you are working in code you could use a ORM to deal with basic CRUD stuff.

t-sql string & table column manipulation

DETAILS table has following columns
Title First Second Third Fourth Fifth
------------------------------------------
A null null null null null
input variable
--------------
#columns = 'Title, Third, Fourth'
I want to generate ouput as
#allcolumns = 'Title, Third, Fourth, First, Second, Fifth'
Variable #allcolumns will contain all columns from DETAILS table but with #columns first and then the remaining columns
So for instance if
#columns = 'Title, Fourth,Fifth'
then output will be
#allcolumns = 'Title, Fourth, Fifth, First, Second, Third'
Thanks
This should work:
DECLARE #columns VARCHAR(max);
DECLARE #allcolumns VARCHAR(max);
SET #columns = 'Title,Fourth,Fifth';
SET #allcolumns = #columns;
SELECT #allcolumns = #allcolumns + ',' + column_name FROM
INFORMATION_SCHEMA.columns WHERE
table_name = 'DETAILS' AND
CHARINDEX(column_name, #columns) = 0;
SELECT #allcolumns;
GO
An additional thought: if you want to create a SELECT statement to select the columns in the order generated by the above code, you could do this:
DECLARE #sql VARCHAR(max);
SET #sql = 'SELECT ' + #allcolumns + ' FROM DETAILS';
EXEC(#sql);
...although I can't see why you would want to do that.
There are many ways to do this. Being your question is rather general, I would suggest looking at the following link and using your INFORMATION_SCHEMA views if using SQL Server.
http://www.simple-talk.com/sql/t-sql-programming/concatenating-row-values-in-transact-sql/
First and most importantly, why not simply return columns First through Fifth and let the calling code determine which columns to use? The SQL language in general was not designed for dynamic column determination and generation. It presumes that the calling code handles the determination of the columns that should be returned. Further, calling code should never depend on the column order in a query and therefore the order of the columns in the output should make no difference. Given that, you should do this type of manipulation in a middle-tier component or reporting tool.
Second, while it is possible to solve this type of problem in T-SQL, it should not be done. T-SQL is awful for string manipulation.
Lastly, if this is the type of query you need to build to get the proper information from your database schema, you might need to re-evaluate your database schema. When you start running into more and more complicated queries in order to retrieve the information you want, it is indicative of a schema that is out of touch with the business needs.