Must declare the scalar variable table - sql

I have the following SQL:
DECLARE #HospitalReport TABLE (Registrator VARCHAR (20))
INSERT INTO #HospitalReport (Registrator)
VALUES("64")
SELECT
#HospitalReport.Registrator
FROM
#HospitalReport
IF Registrator > 0
BEGIN
SELECT
Database.dbo.Users.Firstname, Database.dbo.Users.Lastname
FROM
StradaAnv.dbo.Anvandare
WHERE
Id = Registrator
IF Firstname != NULL AND Lastname != NULL
BEGIN
UPDATE #HospitalReport
SET Registrator = Firstname + ' ' + Lastname
WHERE Registrator = Registrator
END
END
SELECT * FROM #HospitalReport
When I run this code, I get the following error:
Msg 137, Level 16, State 1, Line 9
Must declare the scalar variable "#HospitalReport"
What I see, I already have declared #HospitalReport as a table?

Don't split everything out into procedural steps. Tell the system what you want, not how to do it:
DECLARE #HospitalReport TABLE (Registrator VARCHAR (20))
INSERT INTO #HospitalReport (Registrator)
VALUES("64")
UPDATE H
SET Registrator = Firstname + ' ' + Lastname
FROM
#HospitalReport H
INNER JOIN
StradaAnv.dbo.Anvandare A
ON
H.Registrator = A.Registrator
WHERE A.Firstname IS NOT NULL AND
A.Lastname IS NOT NULL
SELECT * FROM #HospitalReport
I.e. I'm not first querying the table. Then seeing whether particular columns are not null1. Then deciding whether or not to perform an update. I'm describing the entire operation in a single query and then letting the optimizer work out how best to perform this task.
1Which, as shown above, should be done using the IS NULL operator rather than != since NULL is neither equal not not equal to NULL

Related

SQL Loop through tables and columns to find which columns are NOT empty

I created a temp table #test containing 3 fields: ColumnName, TableName, and Id.
I would like to see which rows in the #test table (columns in their respective tables) are not empty? I.e., for every column name that i have in the ColumnName field, and for the corresponding table found in the TableName field, i would like to see whether the column is empty or not. Tried some things (see below) but didn't get anywhere. Help, please.
declare #LoopCounter INT = 1, #maxloopcounter int, #test varchar(100),
#test2 varchar(100), #check int
set #maxloopcounter = (select count(TableName) from #test)
while #LoopCounter <= #maxloopcounter
begin
DECLARE #PropIDs TABLE (tablename varchar(max), id int )
Insert into #PropIDs (tablename, id)
SELECT [tableName], id FROM #test
where id = #LoopCounter
set #test2 = (select columnname from #test where id = #LoopCounter)
declare #sss varchar(max)
set #sss = (select tablename from #PropIDs where id = #LoopCounter)
set #check = (select count(#test2)
from (select tablename
from #PropIDs
where id = #LoopCounter) A
)
print #test2
print #sss
print #check
set #LoopCounter = #LoopCounter + 1
end
In order to use variables as column names and table names in your #Check= query, you will need to use Dynamic SQL.
There is most likely a better way to do this but I cant think of one off hand. Here is what I would do.
Use the select and declare a cursor rather than a while loop as you have it. That way you dont have to count on sequential id's. The cursor would fetch fields columnname, id and tablename
In the loop build a dynamic sql statement
Set #Sql = 'Select Count(*) Cnt Into #Temp2 From ' + TableName + ' Where ' + #columnname + ' Is not null And ' + #columnname <> '''''
Exec(#Sql)
Then check #Temp2 for a value greater than 0 and if this is what you desire you can use the #id that was fetched to update your #Temp table. Putting the result into a scalar variable rather than a temp table would be preferred but cant remember the best way to do that and using a temp table allows you to use an update join so it would well in my opinion.
https://www.mssqltips.com/sqlservertip/1599/sql-server-cursor-example/
http://www.sommarskog.se/dynamic_sql.html
Found a way to extract all non-empty tables from the schema, then just joined with the initial temp table that I had created.
select A.tablename, B.[row_count]
from (select * from #test) A
left join
(SELECT r.table_name, r.row_count, r.[object_id]
FROM sys.tables t
INNER JOIN (
SELECT OBJECT_NAME(s.[object_id]) table_name, SUM(s.row_count) row_count, s.[object_id]
FROM sys.dm_db_partition_stats s
WHERE s.index_id in (0,1)
GROUP BY s.[object_id]
) r on t.[object_id] = r.[object_id]
WHERE r.row_count > 0 ) B
on A.[TableName] = B.[table_name]
WHERE ROW_COUNT > 0
order by b.row_count desc
How about this one - bitmask computed column checks for NULLability. Value in the bitmask tells you if a column is NULL or not. Counting base 2.
CREATE TABLE FindNullComputedMask
(ID int
,val int
,valstr varchar(3)
,NotEmpty as
CASE WHEN ID IS NULL THEN 0 ELSE 1 END
|
CASE WHEN val IS NULL THEN 0 ELSE 2 END
|
CASE WHEN valstr IS NULL THEN 0 ELSE 4 END
)
INSERT FindNullComputedMask
SELECT 1,1,NULL
INSERT FindNullComputedMask
SELECT NULL,2,NULL
INSERT FindNullComputedMask
SELECT 2,NULL, NULL
INSERT FindNullComputedMask
SELECT 3,3,3
SELECT *
FROM FindNullComputedMask

SQL: SAP Hana if parameter is null, ignore where

I'm passing 3 parameters into my Hana Stored Procedure to use as WHERE clauses, and if the parameter is null, I want the procedure to behave as though that condition doesn't exist.
example:
if one of the input parameters is deviceType.
SELECT TOP 5 DISTINCT USERS FROM MYTABLE
WHERE USERDEVICE = deviceType;
if deviceType is null, query should simply be
SELECT TOP 5 DISTINCT USERS FROM MYTABLE.
I know I can achieve this with if statements, but is there another way to do it?
Basically, the requirement is to not apply any condition is deviceType IS NULL. Instead of altering the query dynamically, you could just construct a condition that always returns true in such a situation by using the logical or operator:
SELECT TOP 5 DISTINCT USERS
FROM MYTABLE
WHERE deviceType IS NULL OR USERDEVICE = deviceType;
When using SQLScript you can use the APPLY_FILTER() function.
E.g.
drop procedure getTopUsers;
create procedure getTopUsers (IN filter_cond NVARCHAR(200)) as
begin
vUsers = SELECT DISTINCT user_name, creator FROM USERS;
if (:filter_cond is NULL) then
TopUsers = select TOP 5 user_name FROM :vUsers;
else
tTopUsers = APPLY_FILTER(:vUsers, :filter_cond);
TopUsers = SELECT TOP 5 user_name FROM :tTopUsers;
end if;
SELECT user_name FROM :TopUsers;
end;
call getTopUsers ('CREATOR != ''SYS'' ');
call getTopUsers (NULL);
DECLARE #deviceType VARCHAR(100)
DECLARE #SQL VARCHAR(256)
,#sql1 VARCHAR(256) = 'WHERE USERDEVICE = ''' + #deviceType + ''''
SET #SQL = 'SELECT TOP 5 DISTINCT USERS FROM MYTABLE'
SET #SQL = CASE
WHEN #deviceType IS NULL
THEN #SQL
ELSE #SQL + ' ' + #sql1
END
EXEC (#SQL)

How to UPDATE all columns of a record without having to list every column

I'm trying to figure out a way to update a record without having to list every column name that needs to be updated.
For instance, it would be nice if I could use something similar to the following:
// the parts inside braces are what I am trying to figure out
UPDATE Employee
SET {all columns, without listing each of them}
WITH {this record with id of '111' from other table}
WHERE employee_id = '100'
If this can be done, what would be the most straightforward/efficient way of writing such a query?
It's not possible.
What you're trying to do is not part of SQL specification and is not supported by any database vendor. See the specifications of SQL UPDATE statements for MySQL, Postgresql, MSSQL, Oracle, Firebird, Teradata. Every one of those supports only below syntax:
UPDATE table_reference
SET column1 = {expression} [, column2 = {expression}] ...
[WHERE ...]
This is not posible, but..
you can doit:
begin tran
delete from table where CONDITION
insert into table select * from EqualDesingTabletoTable where CONDITION
commit tran
be carefoul with identity fields.
Here's a hardcore way to do it with SQL SERVER. Carefully consider security and integrity before you try it, though.
This uses schema to get the names of all the columns and then puts together a big update statement to update all columns except ID column, which it uses to join the tables.
This only works for a single column key, not composites.
usage: EXEC UPDATE_ALL 'source_table','destination_table','id_column'
CREATE PROCEDURE UPDATE_ALL
#SOURCE VARCHAR(100),
#DEST VARCHAR(100),
#ID VARCHAR(100)
AS
DECLARE #SQL VARCHAR(MAX) =
'UPDATE D SET ' +
-- Google 'for xml path stuff' This gets the rows from query results and
-- turns into comma separated list.
STUFF((SELECT ', D.'+ COLUMN_NAME + ' = S.' + COLUMN_NAME
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = #DEST
AND COLUMN_NAME <> #ID
FOR XML PATH('')),1,1,'')
+ ' FROM ' + #SOURCE + ' S JOIN ' + #DEST + ' D ON S.' + #ID + ' = D.' + #ID
--SELECT #SQL
EXEC (#SQL)
In Oracle PL/SQL, you can use the following syntax:
DECLARE
r my_table%ROWTYPE;
BEGIN
r.a := 1;
r.b := 2;
...
UPDATE my_table
SET ROW = r
WHERE id = r.id;
END;
Of course that just moves the burden from the UPDATE statement to the record construction, but you might already have fetched the record from somewhere.
How about using Merge?
https://technet.microsoft.com/en-us/library/bb522522(v=sql.105).aspx
It gives you the ability to run Insert, Update, and Delete. One other piece of advice is if you're going to be updating a large data set with indexes, and the source subset is smaller than your target but both tables are very large, move the changes to a temporary table first. I tried to merge two tables that were nearly two million rows each and 20 records took 22 minutes. Once I moved the deltas over to a temp table, it took seconds.
If you are using Oracle, you can use rowtype
declare
var_x TABLE_A%ROWTYPE;
Begin
select * into var_x
from TABLE_B where rownum = 1;
update TABLE_A set row = var_x
where ID = var_x.ID;
end;
/
given that TABLE_A and TABLE_B are of same schema
It is possible. Like npe said it's not a standard practice. But if you really have to:
1. First a scalar function
CREATE FUNCTION [dte].[getCleanUpdateQuery] (#pTableName varchar(40), #pQueryFirstPart VARCHAR(200) = '', #pQueryLastPart VARCHAR(200) = '', #pIncludeCurVal BIT = 1)
RETURNS VARCHAR(8000) AS
BEGIN
DECLARE #pQuery VARCHAR(8000);
WITH cte_Temp
AS
(
SELECT
C.name
FROM SYS.COLUMNS AS C
INNER JOIN SYS.TABLES AS T ON T.object_id = C.object_id
WHERE T.name = #pTableName
)
SELECT #pQuery = (
CASE #pIncludeCurVal
WHEN 0 THEN
(
STUFF(
(SELECT ', ' + name + ' = ' + #pQueryFirstPart + #pQueryLastPart FROM cte_Temp FOR XML PATH('')), 1, 2, ''
)
)
ELSE
(
STUFF(
(SELECT ', ' + name + ' = ' + #pQueryFirstPart + name + #pQueryLastPart FROM cte_Temp FOR XML PATH('')), 1, 2, ''
)
) END)
RETURN 'UPDATE ' + #pTableName + ' SET ' + #pQuery
END
2. Use it like this
DECLARE #pQuery VARCHAR(8000) = dte.getCleanUpdateQuery(<your table name>, <query part before current value>, <query part after current value>, <1 if current value is used. 0 if updating everything to a static value>);
EXEC (#pQuery)
Example 1: make all employees columns 'Unknown' (you need to make sure column type matches the intended value:
DECLARE #pQuery VARCHAR(8000) = dte.getCleanUpdateQuery('employee', '', 'Unknown', 0);
EXEC (#pQuery)
Example 2: Remove an undesired text qualifier (e.g. #)
DECLARE #pQuery VARCHAR(8000) = dte.getCleanUpdateQuery('employee', 'REPLACE(', ', ''#'', '''')', 1);
EXEC (#pQuery)
This query can be improved. This is just the one I saved and sometime I use. You get the idea.
Similar to an upsert, you could check if the item exists on the table, if so, delete it and insert it with the new values (technically updating it) but you would lose your rowid if that's something sensitive to keep in your case.
Behold, the updelsert
IF NOT EXISTS (SELECT * FROM Employee WHERE ID = #SomeID)
INSERT INTO Employee VALUES(#SomeID, #Your, #Vals, #Here)
ELSE
DELETE FROM Employee WHERE ID = #SomeID
INSERT INTO Employee VALUES(#SomeID, #Your, #Vals, #Here)
you could do it by deleting the column in the table and adding the column back in and adding a default value of whatever you needed it to be. then saving this will require to rebuild the table

How to use declared table values to delete from a table?

I'm passing a delimited string to a stored procedure that enters the values into the declared table when it runs into the delimiter,
Here is my Stored Procedure.
Alter PROCEDURE s_BulkDeleteTest
(
#IDString VarChar(200)
)
AS
-- Creating Variables
DECLARE #numberLength int
DECLARE #numberCount int
DECLARE #TheIDs VarChar(200)
DECLARE #sTemp VarChar(100) -- to hold single characters
-- Creating a temp table
DECLARE #T TABLE
(
TheIDs VarChar(500)
)
--Initializing Variables for counting
SET #numberLength = LEN (#IDString)
SET #numberCount = 1
SET #TheIDs = ''
--Start looping through the keyword ids
WHILE (#numberCount <= #numberLength)
BEGIN
SET #sTemp = SUBSTRING (#IDString, #numberCount, 1)
IF (#sTemp = ',')
BEGIN
INSERT #T(TheIDs) VALUES (#TheIDs)
SET #TheIDs = ''
END
IF (#sTemp <> ',')
BEGIN
SET #TheIDs = #TheIDs + #sTemp
END
SET #numberCount = #numberCount + 1
END
This all works fine for adding the values to the #T table, but then I added this..
delete from [Subjects]
where (select TheIDs from #T) = SubjectID
that threw an error about there being more than one value in the declared table #T.
So I was wondering how can I use the values in #T and delete all those ID's from my Subjects table.
If TheIDs has any null values using IN operator will delete unexpect rows. I would suggest using EXISTS operator something like this...
DELETE FROM [Subjects]
WHERE EXISTS
(SELECT 1
FROM #T
WHERE [Subjects].SubjectId = TheIDs)
You need to use in:
delete from [Subjects]
where SubjectId in (select TheIDs from #T);
A result set with multiple rows cannot be equal to a single value.
EDIT:
The expression (select TheIds from #T) returns a set of values. The = operator works on scalar values, not sets. So, it doesn't normally work with this construct. The in operator compares a scalar to a set. so it does work.
There is one exception. When the subquery returns one row and one column, then it is converted to a scalar value. So, the expression would work if there were one row returned, or if you forced one row, as in:
where SubjectId = (select top 1 TheIDs from #T);
Of course, in would work in this situation as well.

How can I use <> operator and || operator inside a stored procedure

I have created an database file inside my asp.net application. Now from server explorer I tried to write a Stored procedure as follows
CREATE PROCEDURE insertData
(
#ID int,
#Name varchar(50),
#Address varchar(50),
#bit BIT OUTPUT
)
as
begin
declare #oldName as varchar(45)
declare #oldAddress as varchar(45)
set #oldName=(select EmployeeName from Employee where EmployeeName=#Name)
set #oldAddress=(select Address from Employee where Address=#Address)
if(#oldName <> #Name | #oldAddress <> #Address)
insert into Employee(EmpID,EmployeeName,Address)values(#ID,#Name,#Address)
SET #bit = 1
END
But this is giving me an error when I am saving it like Incorrect syntax near <..
There are several things wrong here
if(#oldName <> #Name | #oldAddress <> #Address)
Won't work - maybe try
if #oldName <> #Name OR #oldAddress <> #Address
Of course, this will never be true because of the way the two queries above (which could and should have just been one query assigning both variables) make sure that the variables are always equal.
I.e.:
set #oldName=(select EmployeeName from Employee where EmployeeName=#Name)
What can #oldName be, if not equal to #Name? (Okay, it could be NULL, but then <> is the wrong operator to use if NULL is what you're checking for)
I think that what you wanted to write here was:
select #oldName=EmployeeName,#oldAddress = Address from Employee where EmpID = #ID
You should use OR and not |
You can also do this instead of querying and checking each value separately, this will insert new row if name and/or address do not match for given empid
IF NOT EXISTS (
select * from Employee
where EmpID = #ID AND EmployeeName = #Name AND Address = #Address)
insert into Employee(EmpID,EmployeeName,Address)values(#ID,#Name,#Address)
SET #bit = 1
END
You can use != instead of "<>" and you can use Or instead of "|".