Stored procedure to create or update - sql-server

I am working on creating a Web API which will get account as the input parameter, which will have to create / update records in the table in SQL Server. So the web service will need to call the stored procedure which will accept the account. I created a sample table in the database with just two columns called Account and CounterSeq. I am trying to creating a stored procedure to create or update the records in the table.
Each account should have a CounterSeq associated with it. If the Account doesn't exists in the table, create Account name and associate CounterSeq = 001 to it. If the Account name already exists, just update like CounterSeq to CounterSeq + 1
+---------+----------------+
| Account | CounterSeq |
+---------+----------------+
| ABC | 001 |
| DEF | 002 |
+---------+----------------+
For this I create a TableType like this
USE [Demo]
GO
-- Create the data type
CREATE TYPE projectName_TT AS TABLE
(
Account nvarchar(50),
CounterSeq int
)
GO
And the stored procedure as below but I am missing how to insert the new record like for a new Account how to set the CounterSeq to 001 ?
USE [Demo]
GO
ALTER PROCEDURE [dbo].[uspInserorUpdateProjectName]
#projectName_TT AS projectName_TT READONLY
AS
BEGIN
SET NOCOUNT ON;
BEGIN TRANSACTION;
UPDATE prj
SET prj.Account = tt.Account,
prj.CounterSeq = tt.CounterSeq + 1
FROM dbo.[ProjectName] prj
INNER JOIN #projectName_TT tt ON prj.Account = tt.Account
INSERT INTO [dbo].[ProjectName](Account, CounterSeq)
SELECT tt.Account, tt.CounterSeq
FROM #projectName_TT tt
WHERE NOT EXISTS (SELECT 1
FROM [dbo].[ProjectName]
WHERE Account = tt.Account)
COMMIT TRANSACTION;
END;

First, I believe that the table you passed in should only have one field (based on your description If the Account doesn't exists in the Database create Account name and associate CounterSeq 001 to it. If the Account name already exists just update like CounterSeq to CounterSeq+1)
You can use this query (put it in your stored procedure between the BEGIN TRANSACTION, COMMIT TRANSACTION.
MERGE dbo.[ProjectName] prj
USING #projectName_TT tt
ON prj.Account = tt.Account
WHEN MATCHED THEN UPDATE SET prj.CounterSeq = prj.CounterSeq+1
WHEN NOT MATCHED THEN INSERT (Account,CounterSeq)
VALUES (tt.Account, 1);

Related

SQL Server view columns have wrong case

I have a table I am trying to create a view of to work with a new reporting application that needs specific columns. I would think this should be straightforward, but the column names are in the wrong case. Specifically, one column is Customer_ID instead of changing to customer_id as specified in the SQL statement used to create the view.
The original table is: Customer_ID | CustomerAddress | CustomerPhone
The view needs to be: customer_id | customer_address | customer_phone
The view is: Customer_ID | customer_address | customer_phone
The command to create the view is:
BEGIN
SET NOCOUNT ON;
DECLARE #sql_customers nvarchar(max) = 'CREATE VIEW customers AS SELECT
oldcustomers.Customer_ID as customer_id,
oldcustomers.CustomerAddress as customer_address,
oldcustomers.CustomerPhone as customer_phone
FROM oldcustomers'
EXECUTE sp_executesql #sql_customers
END
There's no need to use aliases to change the case unless you're using a case sensitive collation.
The following should do the trick w/o having to jump through hoops...
CREATE VIEW customers
AS
SELECT
oc.customer_id,
oc.customeraddress,
oc.customerphone
FROM
dbo.oldcustomers oc;

Stored procedure using table with recipients

I have a table I would like split and emailed to the corresponding staff member of that department, I have two tables, Table 1 contains all the transaction data against the department and is live, Table 2 is static which essentially lists the staff member who is responsible for the each department.
I need to split up table 1 by Department then lookup the email for the corresponding staff member from table2 and send the split table.
Table 1:
| Customer | ? | Department
| Customer | ? | Department1
| Customer | ? | Department2
Table2:
| Department | Staff | Email
| Department1 | Staff1 | Email
| Department2 | Staff2 | Email
I was wondering, would it be possible to create a stored procedure to do this or would I have to create a subscription in SSRS for each individual staff member?
Thanks,
Neil
I would thoroughly recommend making a simple SSRS report and distributing it via a Data Driven Subscription. The queries below will get you started on your data extracts and you can follow a guide here on how to set up an SSRS Data Driven Subscription.
They are very simple to create, you only need the one subscription to send an email to every Department and they are very easy to maintain, even by someone else with no idea what it does.
declare #t1 table(Cust nvarchar(100)
,Cols nvarchar(100)
,Dept nvarchar(100)
)
declare #t2 table(Dept nvarchar(100)
,Staff nvarchar(100)
,Email nvarchar(100)
)
insert into #t1 Values
('Customer','?','Department1')
,('Customer','?','Department2')
,('Customer','?','Department3')
insert into #t2 Values
('Department1','Staff1','Email1')
,('Department2','Staff2','Email2')
,('Department3','Staff3','Email3')
-- Use this query in your Data Driven Subscription to generate the list of Departments and their respective Emails:
select distinct t1.Dept
,t2.Email
from #t1 t1
left join #t2 t2
on(t1.Dept = t2.Dept)
-- Then use this query in your report to list out the contents of Table 1, matching the #SSRSDeptParameter value in the Data Driven Subscription options.
select t1.Cust
,t1.Cols
,t1.Dept
,t2.Email
from #t1 t1
left join #t2 t2
on(t1.Dept = t2.Dept)
where #t1.Dept = #SSRSDeptParameter

How to update last modified user using windows auth info?

I've a table sales containing sales records in SQL Server. I need a a column modified_by which shows the actor (user) of the last modification. Column modified_by should be filled by the user's ID related to the Windows Authentication.
id content modified_by
----------- ----------- -----------
1 foo Tom
2 bar Jack
If Tom updates Jack's record, then for the record 2, the column modified_by will show Tom instead.
This UPDATE action should be done automotically by the server for every record modification. Can it be possible ? Should I use trigger to do it ?
We can use TRIGGER to check rows after INSERT or UPDATE statement
CREATE TRIGGER dbo.after_update ON dbo.sales
AFTER INSERT, UPDATE
AS
UPDATE dbo.sales
SET modified_by = SYSTEM_USER
FROM inserted
WHERE inserted.id = dbo.sales.id
MSDN: CREATE TRIGGER (Transact-SQL)
update table
set lastmodifiedby =suser_sname()
More info here
you also can create a table with last modified as default ..
create table tt
(
id int,
lastmodifiedby varchar(100) default suser_sname()
)

insert into 2 joined tables in a single effecient query with mssql

I am trying to insert some data into 2 different tables in my ms sql database (ms sql server 2012).
Consider the following 2 tables, that contains information about polls and their choices.
+----------------+
| Polls |
+----------------+
| pollID | The id of a poll (auto increment)
| memberID | The id of the member, owning the poll
| pollTitle | The title/question of the poll
| date | The date of the poll
+----------------+
+----------------+
| PollChoices |
+----------------+
| pollChoiceID | The id of a poll choice (auto increment)
| pollID | The id of the poll, that include this choice
| pollChoice | The name/title of the poll choice
+----------------+
How can I make a query, inserting the data in the most effecient way?
I can always make 2 queries, but can't really figure out how to do it with a single one.
Óne of the main issues for me in this case is getting the id of the poll when inserting the pollchoices.. how can i get this newly inserted "pollID" (auto increment) and use it in the same query?
Also, is it neccesary to make use of transactions or stored procedures (I've read this somewhere)?
Any help will be greatly appreciated.
I don't think what you are aking is possible.
The best way I think to perform the operation you describe, is to wrap it inside a stored procedure. You can use SCOPE_IDENTITY to get the ID of the previously added record and a TRANSACTION together with a TRY-CATCH block to ensure that both insert queries are executed or none at all.
CREATE PROCEDURE [dbo].[usp_InsertPoll] (
-- sproc declaration here, including the following parameters:
-- #memberID, #pollTitle, #date, #pollChoiceID, #pollChoice
)
AS BEGIN
DECLARE #pollID INT
BEGIN TRANSACTION;
BEGIN TRY
INSERT INTO Polls (memberID, pollTitle, date)
VALUES (#memberID, #pollTitle, #date)
-- Get the last identity value inserted into an identity column in the same scope
SET #pollID = SCOPE_IDENTITY();
INSERT INTO PollChoices(pollChoiceID, pollID, pollChoice )
VALUES (#pollChoiceID, #pollID, #pollChoice)
END TRY
BEGIN CATCH
IF ##TRANCOUNT > 0
ROLLBACK TRANSACTION;
END CATCH;
IF ##TRANCOUNT > 0
COMMIT TRANSACTION;
END
Try this query
declare #npi int
INSERT INTO Polls (memberID, pollTitle, date)
VALUES (#memberID, #pollTitle, #date)
set #npi=
(select top 1 PollId
from Polls
order by PollId desc)
INSERT INTO PollChoices(pollChoiceID, pollID, pollChoice )
VALUES (#pollChoiceID, #npi, #pollChoice)

Storing DATABASE_PRINCIPAL_ID in sql server table

We're using stored procedures to restrict the access of some of our database users. They need access to specific parts of the database (not just tables/views, but also specific rows), and the sproc should check if the user is allowed to see the table rows he's requesting.
To store the authorization rules, we're planning to use a table dbo.AuthRules like that:
|ID|UserId |AccessFrom |AccessTo | ...
===============================================
| 1| 1| 01.01.2013 | 31.12.2013 | ...
| 2| 2| 31.05.2012 | 31.12.2015 | ...
The stored procedure would then query that table to check if the current user has access to the requested data. To make it clear: We cannot just use GRANT PERMISSION because we need fine-grained access rules down to the rows in the DB.
We're not sure about the UserId column. The best solution would be some kind of foreign key to the system view sys.database_principals, but there are no foreign keys to views.
Should we just store the principal_id column of sys.database_principals, without any constraint?
Would it be better to store the name column instead of principal_id?
Are there other options to store a reference to the DB user?
Q: Should we just store the principal_id column of sys.database_principals, without any constraint?
A: If you are evaluating only a constraint-based solution, you don't really have any choice. However, if you are willing to write a trigger, that would be the way to at least 'virtually' create constraint. Otherwise you would have to reference system sysowners table and this isn't possible without some hacking.
Q: Would it be better to store the name column instead of principal_id?
A: To answer that you have to ask the what-if question. What if principal_id changes? What if name changes? What about updates/deletes? I would always rather have an id-based solution.
Q: Are there other options to store a reference to the DB user?
A: As stated in answer#1, I don't think there are any possibilities except creating a trigger...
Consider this design method.
USE "master";
-- Create our test logins (don't forget to tidy these up later!)
CREATE LOGIN "batman"
WITH PASSWORD = N''
, CHECK_EXPIRATION = OFF
, CHECK_POLICY = OFF;
CREATE LOGIN "robin"
WITH PASSWORD = N''
, CHECK_EXPIRATION = OFF
, CHECK_POLICY = OFF;
USE "playdb";
-- Create the corresponding users
CREATE USER "batman"
FOR LOGIN "batman";
CREATE USER "robin"
FOR LOGIN "robin";
-- Grant them some select permissions
EXEC sp_addrolemember N'db_datareader', N'batman';
EXEC sp_addrolemember N'db_datareader', N'robin';
-- Create our "user access" table
DECLARE #users table (
username sysname NOT NULL
, access_from date NOT NULL DEFAULT '1900-01-01' -- Deliberately not allowing nulls and setting the widest possible value range
, access_to date NOT NULL DEFAULT '9999-12-31' -- this will make our queries so much easier!
);
-- Give one user full permissions
INSERT INTO #users (username)
VALUES ('batman');
-- Limit what this chappy can see
INSERT INTO #users (username, access_from, access_to)
VALUES ('robin', '2011-01-01', '2012-01-01');
-- Example data table
DECLARE #data table (
date_field date NOT NULL
);
-- ...with example data
INSERT INTO #data (date_field)
VALUES ('2010-01-01')
, ('2011-01-01')
, ('2012-01-01')
, ('2012-01-01')
, ('2013-01-01')
, ('2014-01-01');
-- Let's mimic our full access user
SETUSER 'batman';
SELECT System_User As current_username
, *
FROM #data As data
WHERE EXISTS ( -- Limit the records to only those this user is allowed to see
SELECT *
FROM #users
WHERE username = System_User
AND data.date_field >= access_from
AND data.date_field < access_to
);
SETUSER; -- Reset user
-- Imitate the chap with restricted access
SETUSER 'robin';
-- Exacty same query as before
SELECT System_User As current_username
, *
FROM #data As data
WHERE EXISTS (
SELECT *
FROM #users
WHERE username = System_User
AND data.date_field >= access_from
AND data.date_field < access_to
);

Resources