TSQL query to end user - sql

I have written a parameterized transact SQL query for a member of our finance department and several times during the month I run it and copy the raw output with headers into excel for him. Now that department is being regionalised and I've got several finance departments all wanting the same thing.
I know that SSRS will be deployed eventually but our infrastructure team are building a new environment and don't want any new installations in the 'old world' for the moment.
I just need a way to give select individuals access to run that parameterized query against the database. I had thought about turning the query into a view and creating logins for their network accounts with access only to that view but I don't think you can use parameters with views. I wondered if there is a simple interface that can allow them to enter parameters against a stored query or view without using SSRS. It seems so simple but I'm not having much luck finding out.
Sorry if this is a stupid question but I've just moved from server admin to a DBA role and I've only just scratched the surface!

Create a view and called that in SP with Parameter:-
Sample would be
Create View [dbo].[vw_sampleView]
AS
BEGIN
SELECT * FROM tblSample
END
CREATE PROC [dbo].[proc_GetData]
#id int
AS
BEGIN
SELECT * FROM vw_sampleView where id= #id
END
Then this SP retunred filtered data. Grant the permission to execute this SP to different users.
GRANT EXECUTE ON [dbo].[proc_GetData] TO [user_logins]

You can create a UDF which will return a table based on the parameters. For example:
CREATE FUNCTION [dbo].[fnt_myfunction]( #id INT)
RETURNS TABLE
AS
RETURN (
SELECT *
FROM myTable
WHERE id = #id
);
DECLARE #id INT = 1;
SELECT * FROM [dbo].[fnt_myfunction](#id);
Hope that helps.

Related

Stored procedure for writing back to db2 database

Im new to db2 stored procedure, im approaching here for some help/guidance.
I have users who access cognos for reporting.
Recently I got a requirement from one of our clients for writing back to the db2 table based on user provided comment or input through IBM cognos.
I tried below code in db2 and cognos, but it works half way.
The catch is whenever a user provides a fresh entry it gets stored quickly but whenever a user tries to update the same entry, it takes almost 15-20 mins to refresh that record at table level. I won't understand what i can improve on my code here.
create procedure ngetl.new_update_comment (
in #p_job_status_summary_key integer
,in #p_comment varchar(4000)
,in #p_modified_by varchar(25)
)
dynamic result sets 1
begin
declare e1 cursor with return for
select 1
from ngetl.job_status_summary
where job_status_summary_key = 17076
with ur;
if upper(#p_modified_by) like '%IBM%'
or upper(#p_modified_by) like 'V%' then
update ngetl.job_status_summary
set ibm_comment = #p_comment
,modified_by_ibm = #p_modified_by
,timestamp_ibm = current_timestamp
where job_status_summary_key = #p_job_status_summary_key;
else update ngetl.job_status_summary
set sbi_comment = #p_comment
,modified_by_sbi = #p_modified_by
where job_status_summary_key = #p_job_status_summary_key;
end if;
commit;
open e1;
end

SQL script troubles

I am trying to retrieve employeeIDs using the following sql script:
SELECT * from tblEmployees
GO
CREATE PROCEDURE spGetEmployeeID10
AS
BEGIN
SELECT employeeID
FROM tblEmployees
END
The script runs but returns the entire table with all of the columns' names: employeeID, lastName, firstName, Comment. However, it should only return the employeeID column, no? Am I making an obvious mistake? I am using SQL server management studio 2014. Also, whenever I modify a script and try to re-run it again, I get an error saying that the script already exits. Is there a way to simply edit the script and re-run it with the same name?
Thank you!
"Am I making an obvious mistake?" yes, I'm afraid so, the first select statement is returning everything, an the second is creating a stored procedure. My best guess is that you want this:
CREATE PROCEDURE spGetEmployeeID10
AS
BEGIN
SELECT employeeID
FROM tblEmployees
--my guesswork come further and I added next line
where employeeID = 10
END
GO
EXEC spGetEmployeeID10
GO
What you have is two different operations going on.
The first is your select * which will return everything. The second is the stored procedure creation, which will not return anything. That is why you are seeing the select * results and not the results that are in the stored procedure query.

Determine caller within stored proc or trigger

I am working with an insert trigger within a Sybase database. I know I can access the ##nestlevel to determine whether I am being called directly or as a result of another trigger or procedure.
Is there any way to determine, when the nesting level is deeper than 1, who performed the action causing the trigger to fire?
For example, was the table inserted to directly, was it inserted into by another trigger and if so, which one.
As far as I know, this is not possible. Your best bet is to include it as a parameter to your stored procedure(s). As explained here, this will also make your code more portable since any method used would likely rely on some database-specific call. The link there was specific for SQL Server 2005, not Sybase, but I think you're pretty much in the same boat.
I've not tested this myself, but assuming you are using Sybase ASE 15.03 or later, have your monitoring tables monProcessStatement and monSysStatement enabled, and appropriate permissions set to allow them to be accessed from your trigger you could try...
declare #parent_proc_id int
if ##nestlevel > 1
begin
create table #temp_parent_proc (
procId int,
nestLevel int,
contextId int
)
insert into #temp_parent_proc
select mss.ProcedureID,
mss.ProcNestLevel,
mss.ContextID
from monSysStatement mss
join monProcessStatement mps
on mss.KPID = mps.KPID
and mss.BatchID = mps.BatchID
and mss.SPID = mps.SPID
where mps.ProcedureID =##procid
and mps.SPID = ##spid
select #parent_proc_id = (select tpp.procId
from #temp_parent_proc tpp,
#temp_parent_proc2 tpp2
where tpp.nestLevel = tpp2.nestLevel-1
and tpp.contextId < tpp2.contextId
and tpp2.procId = ##procid
and tpp2.nestLevel = ##nestlevel
group by tpp.procId, tpp.contextId
having tpp.contextId = max(tpp.contextId ))
drop table #temp_parent_proc
end
The temp table is required because of the nature of monProcessStatement and monSysStatement.
monProcessStatement is transient and so if you reference it more than once, it may no longer hold the same rows.
monSysStatement is a historic table and is guaranteed to only return an individual rown once to any process accessing it.
if you do not have or want to set permissions to access the monitoring tables, you could put this into a stored procedure you pass ##procid, ##spid, and ##nestlevel to as parameters.
If this also isn't an option, since you cannot pass parameters into triggers, another possible work around would be to use a temporary table.
in each proc that might trigger this...
create table #trigger_parent (proc_id int)
insert into #trigger_parent ##procid
then in your trigger the temp table will be available...
if object_id('#trigger_parent') is not null
set #parent_proc = select l proc_id from #trigger_parent
you will know it was triggered from within another proc.
The trouble with this is it doesn't 'just work'. You have to enforce temp table setup.
You could do further checking to find cases where there is no #trigger_parent but the nesting level > 1 and combine a similar query to the monitoring tables as above to find potential candidates that would need to be updated.

How to reuse code in SQL stored procedures?

We use SQL Server 2005. All our data access is done through stored procedures. Our selection stored procedures always return multiple result sets.
For instance:
CREATE PROCEDURE hd_invoice_select(#id INT) AS
SELECT * FROM Invoice WHERE InvoiceID = #id
SELECT * FROM InvoiceItem WHERE InvoiceID = #id
SELECT * FROM InvoiceComments WHERE InvoiceID = #id
RETURN
Our application's data access layer builds an object graph based on the results (O/R Mapper style).
The problem I have is that we have many different invoice selection stored procs. They all return the same structure, only for different selection criteria. For instance, I also have:
CREATE PROCEDURE hd_invoice_selectAllForCustomer(#customerID INT) AS
SELECT * FROM Invoice WHERE CustomerID = #customerID
SELECT * FROM InvoiceItem WHERE InvoiceID IN
(SELECT InvoiceID FROM Invoice WHERE CustomerID = #customerID)
SELECT * FROM InvoiceComments WHERE InvoiceID = #id
(SELECT InvoiceID FROM Invoice WHERE CustomerID = #customerID)
RETURN
and I have many others including:
hd_invoice_selectActive()
hd_invoice_selectOverdue()
hd_invoice_selectForMonth(#year INT, #month INT)
and I have the same pattern for a lot of concepts (Customers, Employees, etc)
We end up copying a lot of code and maintenance is really hard. When the "structure" of a concept changes, we have to go and fix all procs and it's very error prone.
So my question is: What is the best way to reuse the code in the scenario?
We came up with a solution that uses temp tables. But it's not very elegant. I'll let you share your ideas and if necessary I will post the detail of my solution in an upcoming post to get your comments on that approach.
Thanks
Posting this as a second answer because it is a different approach. If you are using SQL Server 2008:
CREATE TYPE InvoiceListTableType AS TABLE
(
InvoiceId INT
);
GO
CREATE PROCEDURE hd_invoice_selectFromTempTable
(
#InvoiceList InvoiceListTableType READONLY
)
AS
BEGIN
SELECT * FROM Invoice WHERE InvoiceID IN
(SELECT InvoiceId FROM #InvoiceList)
SELECT * FROM InvoiceItem WHERE InvoiceID IN
(SELECT InvoiceId FROM #InvoiceList)
SELECT * FROM InvoiceComments WHERE InvoiceID IN
(SELECT InvoiceId FROM #InvoiceList)
RETURN
END
GO
CREATE PROCEDURE hd_invoice_select(#id INT) AS
BEGIN
DECLARE #InvoiceList AS InvoiceListTableType;
SELECT id AS ID
INTO #InvoiceList
EXEC hd_invoice_selectFromTempTable(#InvoiceList)
RETURN
END
GO
CREATE PROCEDURE hd_invoice_selectAllForCustomer(#customerID INT) AS
BEGIN
DECLARE #InvoiceList AS InvoiceListTableType;
SELECT invoiceID as ID
INTO #InvoiceList
FROM Invoice WHERE CustomerID = #customerID
EXEC hd_invoice_selectFromTempTable(#InvoiceList)
RETURN
END
GO
CREATE PROCEDURE hd_invoice_selectAllActive AS
BEGIN
DECLARE #InvoiceList AS InvoiceListTableType;
SELECT invoiceID as ID
INTO #InvoiceList
FROM Invoice WHERE Status = 10002
EXEC hd_invoice_selectFromTempTable(#InvoiceList)
RETURN
END
GO
The "best" way for this specific scenario would be to use some sort of code generation. Come up with some sort of convention and plug it into a code generator.
Have you tried putting more than 1 query parameter type in the list of parameters for your main proc? I only wrote the proc to cover the Invoice table, you will need to extend it for your additional tables.
CREATE PROCEDURE hd_invoice_select
(
#id INT = NULL
, #customerId INT = NULL
) AS
BEGIN
SELECT *
FROM Invoice
WHERE
(
#id IS NULL
OR InvoiceID = #id
)
AND (
#customerId IS NULL
OR CustomerID = #customerId
)
RETURN
END
This proc can be called wide open by sending #id and #customerId as NULLs, for a specific InvoiceID based on #id with #customerId as NULL (or just leave it off all together), or for a specific customer based on #customerId leaving #id as NULL or exclude it from the query.
You also should look at views and Table-Valued User-Defined Functions. You can put these in your procs to wrap up some of the logic away from the procs so they can be shared and maintained in a single place. Having some of the logic in views/functions also allows you to deal with the data in a query window as if it were a table.
I'm the person who asked this question in the first place. I'm answering my own question here to let you know the code reuse solution I use and to get your comments on that approach. If this answer gets a lot of up votes, I will select it as the final answer.
This approach works and is simple to use. I don’t know if it has a performance impact because it relies heavily on temporary tables.
For each concept in my application, I have one storec proc like this:
CREATE PROCEDURE hd_invoice_selectFromTempTable AS
/* Get the IDs from an existing #TempInvoiceIDs temporary table */
SELECT * FROM Invoice WHERE InvoiceID IN
(SELECT ID FROM #TempInvoiceIDs)
SELECT * FROM InvoiceItem WHERE InvoiceID IN
(SELECT ID FROM #TempInvoiceIDs)
SELECT * FROM InvoiceComments WHERE InvoiceID IN
(SELECT ID FROM #TempInvoiceIDs)
RETURN
Then I create as many selection stored proc as I need:
CREATE PROCEDURE hd_invoice_select(#id INT) AS
/* Fill #TempInvoiceIDs with matching IDs */
SELECT id AS ID INTO #TempInvoiceIDs
EXEC hd_invoice_selectFromTempTable
RETURN
CREATE PROCEDURE hd_invoice_selectAllForCustomer(#customerID INT) AS
/* Fill #TempInvoiceIDs with matching IDs */
SELECT invoiceID as ID
INTO #TempInvoiceIDs
FROM Invoice WHERE CustomerID = #customerID
EXEC hd_invoice_selectFromTempTable
RETURN
CREATE PROCEDURE hd_invoice_selectAllActive AS
/* Fill #TempInvoiceIDs with matching IDs */
SELECT invoiceID as ID
INTO #TempInvoiceIDs
FROM Invoice WHERE Status = 10002
EXEC hd_invoice_selectFromTempTable
RETURN
What do you think of this approach? It is somewhat similar to AlexKuznetsov's answer but I use temp tables instead of a BLOB parameter.
This is one of the main problems with stored procedures and why people don't like them.
I've never found or seen a way around it.
I have started to use stored procedures generated by Code Generators for my basic CRUD. I use stored procs for reports or complex SQL work.
I have a suggestion unrelated to your question as well - instead of using the IN clause, use the EXISTS clause in your SQL statements.
I've inherited an application that used the temp table approach before and I agree that it's very messy.
On that project we were able to remove a lot of the temp tables by replacing them with Views that contained the desired 'objects' we needed then we updated our stored procedures to query off of those views.
Perhaps that may work in your situation as well.
In some cases I use VIEWS to reuse "code". In cases as filters, active items, outdated things, and so on...
Maybe you should learn to use joins. You could put the basic join of the three tables in a view and just query that with the sp handing the different parameters. Also, you should not in general use select * ever in production code. Only return the few columns you actually need in the circumstances and your whole system will be better performing. Plus you won't have unintended results when people change the structure on you.
I sometimes do it in two steps:
I come up with a list of InvoiceID. Then I call my stored procedure with this list as a parameter.
On 2005 we don't have table valued parameters, so I pack my list of IDs in a binary BLOB and submit it to SQL Server, as described here: Arrays and Lists in SQL Server 2005
You can also submit a list of IDs as a comma-separated list (somewhat slow) or as a concatenation of fixed width string representations (much faster).

SQL Server - Selectively inserting fields into temp table

I am executing a SP within a SP. The SP returns say 10 params. I am interested in only 5 of them. How do I insert only these 5 into the temp table.
The code I have so far:
DECLARE #tmpUnion TABLE
(
UnionCode VARCHAR(10),
UnionDate DATETIME,
UnionPosition VARCHAR(30),
UnionInitFees BIT,
UnionDues BIT
)
--getDetails returns 10 params. I need only these 5
INSERT INTO #tmpUnion
(UnionCode, UnionDate, UnionPosition, UnionInitFees, UnionDues)
EXEC getDetails
#iUserId = #OriginalLoginId
Put the result of getDetails into a tablevar that contains all of the return values, then do your insert off of the additional table.
You might also check out this site for more information on how to share data between stored procedures.
Use OPENROWSET like so:
Select
*
from OPENROWSET('SQLOLEDB','Data Source=Server_name;Trusted_Connection=yes;
Integrated Security=SSPI','Execute yourdb..get_orders')
Now you can easily filter the resultset
Select
employeeid,orderid,orderdate
from
OPENROWSET('SQLOLEDB','Data Source=Server_name;Trusted_Connection=yes;
Integrated Security=SSPI','Execute yourdb..get_orders')
where
orderdate>='19960101' and orderdate<'19970101'
You don't need to create a temp table and you also don't need to worry about the structure of the procedure.
Found here
EDIT: Final solution moved from comments after discussion.
You can't. The table variable must match exactly the structure of waht is being returned.