Automated selection of the correct database within a function - sql-server

I have a function that's working as I'd like it to, but I need to add one thing and I'm not able to make it happen yet.
Here's a massively simplified skeleton of the function that already works:
create function IC
(
#A date
)
returns table
as
return
(
SELECT *
FROM db1.dbo.table a LEFT JOIN
db1.dbo.table2 b
on a.randomfield=b.randomfield
where datefield = #a
)
So now I can use this function to call a date-specific version of the query I wanted. Great! Now the issue is that I want to make this useful over time. datefield is an archive date, and this year's data is in a different database than 2015's data, which is in a different database than 2014's, etc. I want the function to look at #a's year, and then use that to determine which database to query against. I've tried a variety of things that didn't work, but it seems like Dynamic SQL is the answer that everyone arrives at on the interwebs.
I want to use an if...else statement somehow, but haven't been able to put that together. Any suggestions for how to proceed?

Use four-part naming to select from the correct database.
Example
DECLARE #year INT = DATEPART(yyyy, GETDATE())
SELECT CASE #year
WHEN 2016 THEN
(SELECT 1 FROM ARCHIVE2016.dbo.SomeTable)
WHEN 2017 THEN
(SELECT 1 FROM ARCHIVE2017.dbo.SomeTable)
WHEN 2018 THEN
(SELECT 1 FROM ARCHIVE2018.dbo.SomeTable)
END
Just make sure the schema exists, even if it is with empty tables, when placing this inside a function to avoid validation errors.

Related

Oracle stored procedure, SELECT

I'm trying to create procedure, that displays events beetween two dates and my code doesen't compile. In cmd prompt SELECT formula works, but here doesen't want to. Im new to Oracle database, here's the code:
create or replace PROCEDURE SHOW_EVENTS
(
SINCE IN DATE
, TTO IN DATE
) AS
BEGIN
SELECT * FROM events
WHERE EVENT_DATE BETWEEN TO_DATE(SINCE,'dd/mm/yyyy') AND TO_DATE(TTO,'dd/mm/yyyy')
END SHOW_EVENTS;
You have multiple issues. First, the query should be:
SELECT e.*
FROM events e
WHERE e.EVENT_DATE BETWEEN SINCE AND TTO;
There is zero reason to convert a date to a date. The only possible reason would be if you wanted to truncate the date to the nearest day (because Oracle dates have time components). That is better done as:
SELECT e.*
FROM events e
WHERE e.EVENT_DATE BETWEEN trunc(SINCE) AND trunc(TTO);
More importantly, you cannot (easily) do what you want. Not with a stored procedure, because these do not return result sets. Not with a view, because these are not parameterized. Not easily with a function -- writing functions that return a "table" is rather complex in Oracle. It is not for the uninitiated.

Hard code SSRS multi value parameter for testing

I'm trying to test a SQL query in SQL Server Management Studio that normally requires a multivalue parameter from the SSRS report its a part of.
I'm not sure to how hard code a multi value parameter in management studio. The report was created by a vendor, I'm just trying to make it runnable for testing outside of SSRS.
For example the parameter in SSRS is a collection of numbers that the user selects - ie "3100, 3102, 3105" would be the selections for the multivalue parameter called #object_code
I've got something like this - but it's not working.
Declare #Object_Code varchar(100)
Set #object_Code = ('3100','3102','3105')
....really long vendor written query I don't thoroughly understand...
IN(#object_code)
You have to use String-Split function to separate comma separated values.
For example-
Declare #Object_Code varchar(100)
Set #Object_Code = '3100,3102,3105'
....really long vendor written query I dont thoroughly understand...
--T.object_code IN (#object_code)
Inner Join dbo.Split(#Object_Code, ',') as S On S.data = T.object_code
Search your database first for any string-split function.
If you want to create string-split function then follow this -
T-SQL split string
If you use SQL Server 2016 you might want to check out the function STRING_SPLIT.
If you use a lower version of SQL Server and you can't or don't want to create a separate function, the following could be an alternative:
declare #object_code varchar(100);
set #object_code = '3100,3102,3105';
select
ltrim(rtrim(x.par.value('.[1]','varchar(max)'))) as object_code
from (
select convert(xml,'<params><param>' + replace(#object_code,',', '</param><param>') + '</param></params>') as c
) tbl
cross apply
c.nodes('/params/param') x(par);
Everybody seems to be getting hung up on splitting a string that doesn't have to be a string. We're just trouble shooting a query here and need a way to feed it values. It's not important how SSRS does it, just that we can reproduce the result.
Declare #Object_Code table (params varchar(20));
INSERT #object_Code
VALUES ('3100'),('3102'),('3105')
....really long vendor written query I don't thoroughly understand...
IN (SELECT params FROM #object_code)
Then spend some quality time getting to know the query.

Is it possible to create a generic SP to determine Median?

I am using SQL Server 2012. I guess what I am asking is should I continue on the path of researching the ability to create a SP (or UDF, but with #Temp tables probably involved, I was thinking SP) in order to have a reusable object to determine the median?
I hope this isn't too generic of a question, and is hosed, but I have spent some time researching the ability to determine a median value. Some possible hurdles include the need to pass in a string representation of the query that will return the data that I wish to perform the median on.
Anyone attempt this in the past?
Here is a stored proc I use to generate some quick stats.
Simply pass a Source, Measure and/or Filter.
CREATE PROCEDURE [dbo].[prc-Dynamic-Stats](#Table varchar(150),#Fld varchar(50), #Filter varchar(500))
-- Syntax: Exec [dbo].[prc-Dynamic-Stats] '[Chinrus-Series].[dbo].[DS_Treasury_Rates]','TR_Y10','Year(TR_Date)>2001'
As
Begin
Set NoCount On;
Declare #SQL varchar(max) =
'
;with cteBase as (
Select RowNr=Row_Number() over (Order By ['+#Fld+'])
,Measure = ['+#Fld+']
From '+#Table+'
Where '+case when #Filter='' then '1=1' else #Filter end+'
)
Select RecordCount = Count(*)
,DistinctCount = Count(Distinct A.Measure)
,SumTotal = Sum(A.Measure)
,Minimum = Min(A.Measure)
,Maximum = Max(A.Measure)
,Mean = Avg(A.Measure)
,Median = Max(B.Measure)
,Mode = Max(C.Measure)
,StdDev = STDEV(A.Measure)
From cteBase A
Join (Select Measure From cteBase where RowNr=(Select Cnt=count(*) from cteBase)/2) B on 1=1
Join (Select Top 1 Measure,Hits=count(*) From cteBase Group By Measure Order by 2 desc ) C on 1=1
'
Exec(#SQL)
End
Returns
RecordCount DistinctCount SumTotal Minimum Maximum Mean Median Mode StdDev
3615 391 12311.81 0.00 5.44 3.4057 3.57 4.38 1.06400795277565
You may want to take a look at a response that I had to this post. In short, if you're comfortable with C# or VB .NET, you could create a user defined CLR aggregate. We use CLR implementations for quite a few things, especially statistical methods that you may see in other platforms like SAS, R, etc.
This is easily accomplished by creating a User-Defined Aggregate (UDA) via SQLCLR. If you want to see how to do it, or even just download the UDA, check out the article I wrote about it on SQL Server Central: Getting The Most Out of SQL Server 2005 UDTs and UDAs (please note that the site requires free registration in order to read their content).
Or, it is also available in the Free version of the SQL# SQLCLR library (which I created, but again, it is free) available at http://SQLsharp.com/. It is called Agg_Median.
If using SQL Server 2008 or newer (which you are), you can write a function that accepts a table-valued parameter as input.
Create Type MedianData As Table ( DataPoint Int )
Create Function CalculateMedian ( #MedianData MedianData ReadOnly )
Returns Int
As
Begin
-- do something with #MedianData which is a table
End

Creating a new row in SSRS dataset SQL query to use in a report parameter

I am going round in circles with a bit of SQL and would appreciate some help.
I've looked up creating temp tables, nested Select statements (where advice seems to be to avoid these like the plague) and various uses of Case statements but I can't seem to find a solution that works. I'd say I'm beginner level for SQL.
I have a table with 10 relevant records. The query that works to return all the relevant entries in the table is:
SELECT
TblServAct.ServActId
,TblServAct.ServActName
FROM TblServAct
WHERE TblServAct.ServActExclude IS NULL
ORDER BY TblServAct.ServActName
Here is where I run into problems:
When the parameter (#YESNOActivity) = Yes, I want all the rows in the table to be returned. I have managed to do this with a CASE statement
...however when the parameter (#YESNOActivity) = No, I want ONLY ONE row to be returned which doesn't actually exist in the table (and should not be inserted into the actual table). The values that I need to insert are: ServActId = 101 and ServActName = 'Select YES in Parameter2 to filter by Service Activity'
For background, the reason I am doing this is because I have found SSRS report parameters to be especially difficult to conditionally format. I want to use the dataset above to return a message in a parameter (lets call it parameter2) that the user needs to select yes in (#YESNOActivity) in order to see the full selection list in parameter2.
If I can get this to work I can see lots of potential for re-use so all advice appreciated
Thanks
Eileen
I believe this should do the job, just include your parameter in the WHERE clause and UNION it with your own row of data.
SELECT
TblServAct.ServActId
,TblServAct.ServActName
FROM TblServAct
WHERE TblServAct.ServActExclude IS NULL
AND #YESNOActivity = 'Yes'
UNION ALL
SELECT
ServActId = 101
,ServActName = 'Select YES in Parameter2 to filter by Service Activity'
WHERE #YESNOActivity = 'No'
ORDER BY TblServAct.ServActName
One way is to use this query:
SELECT
TblServAct.ServActId
,TblServAct.ServActName
FROM TblServAct
WHERE TblServAct.ServActExclude IS NULL
AND 'Yes' = #YESNOActivity
UNION ALL
SELECT
101 AS ServActId
,'Select YES in Parameter2 to filter by Service Activity' AS ServActName
WHERE 'No' = #YESNOActivity
ORDER BY TblServAct.ServActName
Another way would be to create two data flows and use your variable in a constraint to send the processing to one or the other.
A third way would be to put an expression on the SQL command and use your variable to switch between two SQL statements.

What is a good way to paginate out of SQL 2000 using Start and Length parameters?

I have been given the task of refactoring an existing stored procedure so that the results are paginated. The SQL server is SQL 2000 so I can't use the ROW_NUMBER method of pagination. The Stored proc is already fairly complex, building chunks of a large sql statement together before doing an sp_executesql and has various sorting options available.
The first result out of google seems like a good method but I think the example is wrong in that the 2nd sort needs to be reversed and the case when the start is less than the pagelength breaks down. The 2nd example on that page also seems like a good method but the SP is taking a pageNumber rather than the start record. And the whole temp table thing seems like it would be a performance drain.
I am making progress going down this path but it seems slow and confusing and I am having to do quite a bit of REPLACE methods on the Sort order to get it to come out right.
Are there any other easier techniques I am missing?
There are two SQL Server 2000 compliant answers in this StackOverflow question - skip the accepted one, which is 2005-only:
No, I'm afraid not - SQL Server 2000 doesn't have any of the 2005 niceties like Common Table Expression (CTE) and such..... the method described in the Google link seems to be one way to go.
Marc
Also take a look here
http://databases.aspfaq.com/database/how-do-i-page-through-a-recordset.html
scroll down to Stored Procedure Methods
Depending on your application architecture (and your amount of data, it's structure, DB server load etc.) you could use the DB access layer for paging.
For example, with ADO you can define a page size on the record set (DataSet in ADO.NET) object and do the paging on the client. Classic ADO even lets you use a server side cursor, though I don't know if that scales well (I think this was removed altogether in ADO.NET).
MSDN documentation: Paging Through a Query Result (ADO.NET)
After playing with this for a while there seems to be only one way of really doing this (using Start and Length parameters) and that's with the temp table.
My final solution was to not use the #start parameter and instead use a #page parameter and then use the
SET #sql = #sql + N'
SELECT * FROM
(
SELECT TOP ' + Cast( #length as varchar) + N' * FROM
(
SELECT TOP ' + Cast( #page*#length as varchar) + N'
field1,
field2
From Table1
order by field1 ASC
) as Result
Order by Field1 DESC
) as Result
Order by Field 1 ASC'
The original query was much more complex than what is shown here and the order by was ordered on at least 3 fields and determined by a long CASE clause, requiring me to use a series of REPLACE functions to get the fields in the right order.
We've been using variations on this query for a number of years. This example gives items 50,000 to 50,300.
select top 300
Items.*
from Items
where
Items.CustomerId = 1234 AND
Items.Active = 1 AND
Items.Id not in
(
select top 50000 Items.Id
from Items
where
Items.CustomerId = 1234 AND
Items.Active = 1
order by Items.id
)
order by Items.Id

Resources