Turn SQL Server table variable into real table - sql

I have a temporary table created with:
DECLARE #tbl_fk_errors TABLE (
id int Primary Key IDENTITY(1,1)
, tbl_id int
, tbl_name nvarchar(128)
, tbl_record_id int
, tbl_field nvarchar(20)
, tbl_field_value nvarchar(20)
, jnktn_field_value nvarchar(20)
, jnktn_field nvarchar(20)
, jnktn_tblname nvarchar(128)
That table will be filled in my script. Now I want to output that table into a file and found xp_cmdshell which can do that. But that stored procedure can't take my table var because it opens a new connection to the DB (because of the "bcp").
I need to temporary save that table into my DB (and drop it after, but that's no big deal then).
How can I quickly save a table stored in a table variable as real table in my DB?

select *
into table --destination
from #table --source
As a bonus, wrap a transaction around this to make sure you don't leave a "dangling" table in case of an error. DDL is transactional in SQL Server. On rollback, the table will be removed.

Related

Primary key in a temporary table in SQL server causing ' There is already an object' error

I'm tring to upload data from python to a temp table in database and delete it after use. However, everytime I rerun it, it always gives a error
There is already an object named 'PK_temp' in the database.
It seems very strange to me as I think by dropping the temp table, the primary key I created for this table will also be dropped automatically.
To be more specific, I'm doing this
Step 1: drop the temp table if there is any
run_sql("""IF OBJECT_ID (N'tempdb..#temp') IS NOT NULL drop table #temp""", engine)
Step 2: create the temp table with a primary key and upload data by passing it as xml string
query = """
set nocount on;
declare #temp varchar(max);
select #temp = '{xml_str}';
-- CREATE TEMP TABLES
CREATE TABLE #temp(
id int not null,
CONSTRAINT PK_temp PRIMARY KEY (id asc),
);
-- EXTRACT XML INTO TEMP TABLES
declare #xml xml
declare #hdoc int;
select #xml = #temp
exec sp_xml_preparedocument #hdoc OUTPUT, #xml
insert into #temp (id)
select id
from OPENXML(#hdoc, '/data/row', 2)
with (id int)
exec sp_xml_removedocument #hdoc;
""".format(xml_str = id_xml_str)
run_sql(query, engine)
To be more clear, my run_sql looks like this
def run_sql(sql, engine)
session = Session(engine)
try:
session.execute(clause=sql)
session.commit()
except Exception as e:
session.rollback()
raise e
finally:
session.close()
return
and id_xml_str will be looks like this:
"
<data>\n
<row>\n <index>0</index>\n <id>18511</id>\n </row>\n
<row>\n <index>1</index>\n <id>18671</id>\n </row>\n
<row>\n <index>2</index>\n <id>18711</id>\n </row>\n
<row>\n <index>3</index>\n <id>18833</id>\n </row>\n
<row>\n <index>4</index>\n <id>18965</id>\n </row>\n
</data>"
I was wondering if some one can shed on some light on why this would happen and how to fix this?
BTW, I'm using
python==3.7
sqlalchemy==1.3.20
Just remove the PK name to give the PK a unique auto-generated name.
CREATE TABLE #temp
(
id int not null,
PRIMARY KEY (id asc),
);
Temporary tables have unique names generated for them in TempDb, but primary key constraints are separate objects, and you can't have two constraints with the same name in a single schema in any database.
So if two different sessions try to create a temp table with the same named constraint, one will fail.

Return Identity Key from select statement in a stored procedures

I am running a stored procedure that selects all the records from a database.
Select *
from dbo.Recods
where Active = 1;
but when I run this stored procedure, I do not get the identity key back, just the rest of the columns in the database.
Any ideas?
Thank you
The query to create the table is:
CREATE TABLE [dbo].[Tournaments] (
-- Add the parameters for the stored procedure here
Id int IDENTITY(1,1) PRIMARY KEY,
TournamentName nvarchar(50) NOT NULL,
EntryFee float,
Active int
);

Define relationship to tables as a whole

I need to define a table which basically will contain an Id for a user, and a second column which will list names of tables to which the user has access. I can't think of anyway to define any relationships here in case the original table names change. All the logic will be at the application level. However, I would like to be able to define some sort of constraints. How can I do this? Also, I am open to advice regarding any other way to do this.
I am really confused. Doesn't the grant command do exactly what you want? This assumes that the operations you want are database operations.
If you have a more customized set of operations, then you can keep track of table name changes via DDL triggers.
Here's a detailed code example of how to achieve this using RLS
USE tempdb;
GO
CREATE TABLE dbo.OKTable
(
OKTableID int IDENTITY(1,1) NOT NULL
CONSTRAINT PK_dbo_OKTable PRIMARY KEY,
SecuredInfo varchar(100)
);
GO
INSERT dbo.OKTable (SecuredInfo)
VALUES ('Very'), ('Secret'), ('Stuff');
GO
CREATE TABLE dbo.NotOKTable
(
NotOKTableID int IDENTITY(1,1) NOT NULL
CONSTRAINT PK_dbo_NotOKTable PRIMARY KEY,
SecuredInfo varchar(100)
);
GO
INSERT dbo.NotOKTable (SecuredInfo)
VALUES ('Other'), ('Important'), ('Things');
GO
CREATE SCHEMA [Security] AUTHORIZATION dbo;
GO
CREATE TABLE [Security].PermittedTableUsers
(
PermittedTableUsers int IDENTITY(1,1) NOT NULL
CONSTRAINT PK_Security_PermittedTableUsers
PRIMARY KEY,
UserName sysname,
SchemaName sysname,
TableName sysname
);
GO
INSERT [Security].PermittedTableUsers (UserName, SchemaName, TableName)
VALUES (N'dbo', N'dbo', 'OKTable');
GO
ALTER FUNCTION [Security].CheckUserAccess
(
#SchemaName AS sysname,
#TableName AS sysname
)
RETURNS TABLE
WITH SCHEMABINDING
AS
RETURN SELECT 1 AS CheckUserAccessOutcome
WHERE EXISTS (SELECT 1 FROM [Security].PermittedTableUsers AS ptu
WHERE ptu.UserName = USER_NAME()
AND ptu.SchemaName = #SchemaName
AND ptu.TableName = #TableName);
GO
CREATE SECURITY POLICY OKTableAccessFilter
ADD FILTER PREDICATE [Security].CheckUserAccess (N'dbo', N'OKTable')
ON dbo.OKTable
WITH (STATE = ON);
GO
CREATE SECURITY POLICY NotOKTableAccessFilter
ADD FILTER PREDICATE [Security].CheckUserAccess (N'dbo', N'NotOKTable')
ON dbo.NotOKTable
WITH (STATE = ON);
GO
SELECT * FROM dbo.OKTable;
SELECT * FROM dbo.NotOKTable;
GO
It's described more fully in this link:
https://blog.greglow.com/2019/10/10/sql-how-to-control-access-to-sql-server-tables-by-entries-in-another-table/
Since users are NOT SQL Server Logins, therefore, I guess you can use the DDL trigger to monitor table rename where you can change the table name in your custom security table. But I don't know if you can throw exception within this trigger to prevent table rename (simulating some type of constraint). Also it would be better if you store the table name in each line rather saving comma separated table names in 1 field.
If you can utilize SQL Logins then the Gordan's solution is also applicable, but sometimes you cannot create SQL Logins, in-case if you have different application databases along with hundreds of thousands of users.

Altering SQL table to add column

I currently have a table with four columns - i wanted to add a fifth column but having some trouble.
I open the table in sql server studio management 2008 and i added the column info like so:
CREATE TABLE [dbo].[Case]
(
CaseId UNIQUEIDENTIFIER DEFAULT (newid()) NOT NULL,
CaseNumber NVARCHAR(50) NOT NULL,
CourtId INT NOT NULL,
DateOpened DATETIME NOT NULL,
)
my addition:
CREATE TABLE [dbo].[Case]
(
CaseId UNIQUEIDENTIFIER DEFAULT (newid()) NOT NULL,
CaseNumber NVARCHAR(50) NOT NULL,
CaseName NVARCHAR(50),
CourtId INT NOT NULL,
DateOpened DATETIME NOT NULL,
)
After adding CaseName column, i tried executing the table in Management Studio but i got the error message "There is already an object named 'Case' in the database."
I tried saving and then building my database hoping that the column will be added but that wasn't successful. I tried a New Query and writing the 'Alter table "case" add CaseName nvarchar(50) but again without luck. It shows that the file is changed with the new column because i saved it but after building my overall database it isn't making any changes. Any helpful tips will be great.
You want to ALTER, as follows:
ALTER TABLE [dbo].[Case] ADD CaseName NVARCHAR(50)
Better yet, you can check for the existance of the column first:
if not exists (SELECT 1 FROM sysobjects INNER JOIN syscolumns ON
sysobjects.id = syscolumns.id
WHERE sysobjects.name = N'Case' AND syscolumns.name = N'CaseName')
ALTER TABLE [dbo].[Case] ADD CaseName NVARCHAR(50)
you should try this
ALTER TABLE [dbo].[Case]
ADD CaseName NVARCHAR(50)
You are trying to create another table Case but one already exists that's why you have an error. When you want to edit a table, you have to use Alter table
Use an Alter table statement instead of Create
If you can't get the Alter statement to work for some reason, you could also drop the existing table and create a new one with the new field, but all your existing rows will be lost.
If you're using SSMS, you can Design the table instead of Edit to add the column.
ALTER is what you need to investigate (F1)
An alternative is.
Create a new table
CREATE TABLE [dbo].[Case2]
(
CaseId UNIQUEIDENTIFIER DEFAULT (newid()) NOT NULL,
CaseNumber NVARCHAR(50) NOT NULL,
CourtId INT NOT NULL,
DateOpened DATETIME NOT NULL,
newcolumn INT NULL
)
Move data from existing table into the new one
INSERT INTO [dbo].[Case2]
SELECT * FROM [dbo].[Case]
Then
DROP TABLE [dbo].[Case]
Then in management studio right-click 'Case2' and re-name it 'Case'
I recommend checking for the existence of the column prior to adding it, especially important when you work with migration scripts.
Here is how I usually do it:
IF NOT EXISTS(SELECT * FROM sys.columns WHERE Name = N'ColumnName' AND Object_ID = Object_ID(N'TableName'))
BEGIN
ALTER TABLE [dbo].TableName ADD ColumnName NVARCHAR(512) null
END

Mysql Cross Schema Queries Based on Row Data

So I'm working on a web application which our clients want their user data separated from other client's user data. Since our clients can be collaborative with each other, we can't just create a new instance of the application for each client, since we have to make sure user ids match up and are unique between clients.
Basically there's our "MasterApplication" database which has a "User" table which contains a UserId and the name of the database where to find the remaining user data. Each client has their own database user which only has access to the "MasterApplication" and their own client table.
Here's an example of how it looks.
CREATE DATABASE MasterDatabase
CREATE DATABASE Client1
CREATE DATABASE Client2
CREATE TABLE `MasterDatabase`.`Person` (
`PersonId` INT NOT NULL AUTO_INCREMENT,
`DatabaseName` Varchar(20) NOT NULL,
PRIMARY KEY (`PersonId`)
)
CREATE TABLE `Client1`.`Person` (
`PersonId` INT NOT NULL,
`FirstName` Varchar(20) NOT NULL,
PRIMARY KEY (`PersonId`)
)
CREATE TABLE `Client2`.`Person` (
`PersonId` INT NOT NULL,
`FirstName` Varchar(20) NOT NULL,
PRIMARY KEY (`PersonId`)
)
INSERT INTO MasterDatabase.Person VALUES (1, 'Client1');
INSERT INTO MasterDatabase.Person VALUES (2, 'Client2');
INSERT INTO Client1.Person VALUES (1, 'Matt');
INSERT INTO Client2.Person VALUES (2, 'Scott');
So Client1 would have a database user which has access to MasterDatabase and Client1 tables. Client2 would have a user with access to MasterDatabase and Client2.
I'm hoping there's some way that I can do a cross schema query easily using the data from the "DatabaseName" field in MasterDatabase, but I can't figure it out. I know for the most part I can store the client's database name in the application logic and just insert it into the query, but there will be a few spots which I'll need to join all clients into one query.
My attempt was to do
SELECT *, `DatabaseName` INTO #DB FROM MasterDatabase.Person
LEFT JOIN #DB.Person ON MasterDatabase.Person.PersonId = #DB.Person.PersonId
But that didn't work as I was hoping. Is there maybe someway I can do it with a procedure? I'm also open to other ideas of separating out the user data per client too if you have any.
I think you can do it in stored procedure. It will look something like
DELIMITER //
CREATE DEFINER='root'#'localhost' PROCEDURE GetData(IN user_id INT)
BEGIN
DECLARE db_name varchar(100);
SELECT DatabaseName INTO db_name FROM MasterDatabase.Person WHERE PersonId = user_id;
SET #t1 =CONCAT('SELECT * FROM ',db_name,'.Person' );
PREPARE stmt1 FROM #t1;
EXECUTE stmt1;
END//
UPDATE Sorry, I forgot DEALLOCATE PREPARE stmt1; ; it should be after EXECUTE stmt1;