How to use variable and changing user defined functions in query - sql

I have defined some user defined function in sql server . for example :
select dbo.SumItems(1 , 2) will return 3
and
select dbo.MaxItems(5 , 3) will return 5
and some other functions may be more complicated.
also I keep variables and their formula expressions in a table:
IdVar Title Formula
----- ----- ---------------------
1 Sum dbo.SumItems(#a , #b)
2 Max dbo.maxItems(#a , #b)
I have my parameters in another table :
a b
-- --
1 2
5 3
now i want to join this two tables and get the following result :
Parameter a Parameter b Variable Title Result
----------- ----------- -------------- ------
1 2 Sum 3
1 2 Max 2
5 3 Sum 8
5 3 Max 5
also I have asked my problem from another view here.

Posted something very similar to this yesterday. As you know, you can only perform such function with dynamic sql.
Now, I don't have your functions, so you will have to supply those.
I've done something very similar in the past to calculate a series of ratios in one pass for numerous income/balance sheets
Below is one approach. (However, I'm not digging the 2 parameters ... seems a little limited, but I'm sure you can expand as necessary)
Declare #Formula table (ID int,Title varchar(25),Formula varchar(max))
Insert Into #Formula values
(1,'Sum' ,'#a+#b')
,(2,'Multiply','#a*#b')
Declare #Parameter table (a varchar(50),b varchar(50))
Insert Into #Parameter values
(1,2),
(5,3)
Declare #SQL varchar(max)=''
;with cte as (
Select A.ID
,A.Title
,ParameterA = A
,ParameterB = B
,Expression = Replace(Replace(Formula,'#a',a),'#b',b)
From #Formula A
Cross Join #Parameter B
)
Select #SQL = #SQL+concat(',(',ID,',',ParameterA,',',ParameterB,',''',Title,''',(',Expression,'))') From cte
Select #SQL = 'Select * From ('+Stuff(#SQL,1,1,'values')+') N(ID,ParameterA,ParameterB,Title,Value)'
Exec(#SQL)
-- Optional To Trap Results in a Table Variable
--Declare #Results table (ID int,ParameterA varchar(50),ParameterB varchar(50),Title varchar(50),Value float)
--Insert Into #Results Exec(#SQL)
--Select * from #Results
Returns
ID ParameterA ParameterB Title Value
1 1 2 Sum 3
2 1 2 Multiply 2
1 5 3 Sum 8
2 5 3 Multiply 15

Related

How do u use between operator for range?

This is my table Data
Id Begin End
1 0 1
2 1 3
3 3 4
This is my Query:
DECLARE #abc Float=1.5;
SELECT * FROM dbo.Slab AS s WHERE #abc BETWEEN s.Begin AND s.End
This give me 2 rows. I want to find a row for which #abc parameter is greater than Begin but less or equal to End.For example if #abc=1 i want to select 1 , if #abc=1.5 I want to select 2 , if #abc=3 i want to select 2 , if #abc=0.1 i want to select 1 and so on.
DECLARE #abc Float=1.5;
SELECT * FROM dbo.slab AS s WHERE #abc > s.[begin] and #abc<=s.[end]

How can I update strings within an SQL server table based on a query?

I have two tables A and B. A has an Id and a string with some embedded information for some text and ids from a table C that is not shown
Aid| AString
1 "<thing_5"><thing_6">"
2 "<thing_5"><thing_6">"
Bid|Cid|Aid
1 5 1
2 6 1
3 5 2
4 6 2
I realise this is an insane structure but that is life.
I need to update the strings within A so that instead of having the Cid they have the corresponding Bid (related by the Aid and Bid pairing)
Is this even something I should be thinking of doing in SQL... A has about 300 entries and B about 1200 so not something doing by hand
For clarity I wish for B to remain the same and A to finally look like this
Aid| AString
1 "<thing_1"><thing_2">"
2 "<thing_3"><thing_4">"
This script relies on generating dynamic SQL statements to update the table, then executes those statements.
Taking into account that the cid's are within thing_ and ":
First replaces the cid's using a placeholder ($$$$$$ in this case) to account for the fact that cid's and bid's may overlap (example, changing 3->2 and later 2->1)
Then changes the placeholders to the proper bid
CREATE TABLE #a(aid INT,astr VARCHAR(MAX));
INSERT INTO #a(aid,astr)VALUES(1,'<thing_5"><thing_6">'),(2,'<thing_5"><thing_6">');
CREATE TABLE #rep(aid INT,bid INT,cid INT);
INSERT INTO #rep(bid,cid,aid)VALUES(5,6,1),(6,5,1),(3,5,2),(4,6,2);
DECLARE #cmd NVARCHAR(MAX)=(
SELECT
'UPDATE #a '+
'SET astr=REPLACE(astr,''thing_'+CAST(r.cid AS VARCHAR(16))+'"'',''thing_$$$$$$'+CAST(r.cid AS VARCHAR(16))+'"'') '+
'WHERE aid='+CAST(a.aid AS VARCHAR(16))+';'
FROM
(SELECT DISTINCT aid FROM #a AS a) AS a
INNER JOIN #rep AS r ON
r.aid=a.aid
FOR
XML PATH('')
);
EXEC sp_executesql #cmd;
SET #cmd=(
SELECT
'UPDATE #a '+
'SET astr=REPLACE(astr,''thing_$$$$$$'+CAST(r.cid AS VARCHAR(16))+'"'',''thing_'+CAST(r.bid AS VARCHAR(16))+'"'') '+
'WHERE aid='+CAST(a.aid AS VARCHAR(16))+';'
FROM
(SELECT DISTINCT aid FROM #a AS a) AS a
INNER JOIN #rep AS r ON
r.aid=a.aid
FOR
XML PATH('')
);
EXEC sp_executesql #cmd;
SELECT * FROM #a;
DROP TABLE #rep;
DROP TABLE #a;
Result is:
+-----+----------------------+
| aid | astr |
+-----+----------------------+
| 1 | <thing_6"><thing_5"> |
| 2 | <thing_3"><thing_4"> |
+-----+----------------------+
You could do this with SQL with something like below. It wasn't clear to me how c was related, but you can adjust it as necessary...
create table a (
Aid int null,
AString varchar(25) null)
insert into a values(1,'"<thing_5"><thing_6">"')
insert into a values(2,'"<thing_5"><thing_6">"')
create table b (
Aid int null,
Bid int null,
Cid int null)
insert into b values(1,1,5)
insert into b values(1,2,6)
insert into b values(2,3,5)
insert into b values(2,4,6)
UPDATE Ax
SET Ax.ASTRING = REPLACE(Ax.ASTRING, 'thing_' + cast(cID as varchar(1)),'thing_' + cast(BID as varchar(1)))
FROM A Ax
INNER JOIN Bx
on ax.Aid=bx.Aid
and Ax.AString like '%thing_' + cast(Cid as varchar(1)) + '%'

Sorting results of SQL query just like IN parameter list

I'm doing a query which looks something like
SELECT id,name FROM table WHERE id IN (2,1,4,3)
I'd like to get
id name
2 B
1 A
4 D
3 C
but I'm getting
1 A
2 B
3 C
4 D
Is there any way to sort the query results in the same way as the list I'm including after IN?
Believe me, I have a practical reason that I would need it for ;)
SELECT id,name FROM table WHERE id IN (2,1,4,3)
ORDER BY CASE id
WHEN 2 THEN 1
WHEN 1 THEN 2
WHEN 4 THEN 3
WHEN 3 THEN 4
ELSE 5
END
This might solve your problem.
Solution 2, insert your list into a temp table and get them a running sequence
id, seq(+1 every new row added)
-----------------
2 1
1 2
4 3
3 4
then join 2 table together and order by this seq.
Okay, I did it myself. It's a bit mad but it works ;)
DECLARE #IDs varchar(max)
DECLARE #nr int
DECLARE #znak varchar(1)
DECLARE #index int
DECLARE #ID varchar(max)
SET #IDs='7002,7001,7004,7003'
SET #nr=1
SET #index=1
IF OBJECT_ID('tempdb..#temp') IS NOT NULL DROP TABLE #temp
CREATE TABLE #temp (nr int, id int)
--fill temp table with Ids
WHILE #index<=LEN(#Ids)
BEGIN
set #znak=''
set #ID=''
WHILE #znak<>',' AND #index<=LEN(#Ids)
BEGIN
SET #znak= SUBSTRING(#IDs,#index,1)
IF #znak<>',' SET #ID=#ID+#znak
SET #index=#index+1
END
INSERT INTO #temp(nr,id) VALUES (#nr,CAST(#ID as int))
SET #nr=#nr+1
END
-- select proper data in wanted order
SELECT MyTable.* FROM MyTable
INNER JOIN #temp ON MyTable.id=#temp.id
ORDER BY #temp.nr

TSQL Select all columns without first n from function/procedure

I know this may sounds silly but I would like to create function that will process data from tables with different size.
Lets say I have first table like so:
ID IRR M0 M1
----------------------
1 0 -10 5
2 0 -20 10
3 0 -100 100
4 0 -10 0
And second table like so:
ID IRR M0 M1 M2
----------------------------
1 0 -10 5 60
2 0 -20 10 0
3 0 -100 100 400
4 0 -10 0 10
I would like to create function that will be able to process data from both tables.
I know that first column contains ID, second IRR, rest of columns will hold cash flow for specific month.
Function should be able to process all columns instead of first 2 and store result in second column.
I know that I can get all columns from specific table with:
SELECT COLUMN_NAME
FROM INFORMATION_SCHEMA.columns
WHERE TABLE_NAME = 'First_Table'
Problems begin when I would like to create function that will return those columns as rows.
I can create function like so:
CREATE FUNCTION UnpivotRow (#TableName varchar(50), #FromWhichColumn int, #Row int)
RETURNS #values TABLE
(
id INT IDENTITY(0, 1),
value DECIMAL(30, 10)
)
...
But how this function should look?
I think that ideal for this kind of processing table should look like so:
ProjectID TimePeriod Value
--------------------------------
1 0 -10
1 1 5
2 0 -20
2 1 10
3 0 -100
3 1 100
4 0 -10
4 1 0
I need to unpivot whole table without knowing number of columns.
EDIT:
If this can't be done inside function, then maybe inside a procedure?
This can be done using dynamic SQL to perform the UNPIVOT:
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX),
#colTP as NVARCHAR(MAX)
select #cols = stuff((select ','+quotename(C.name)
from sys.columns as C
where C.object_id = object_id('table1') and
C.name like 'M%'
for xml path('')), 1, 1, '')
set #query = 'SELECT id,
replace(timeperiod, ''M'', '''') timeperiod,
value
from table1
unpivot
(
value
for timeperiod in (' + #cols + ')
) u '
exec(#query)
See SQL Fiddle with Demo.
This solution would have to be placed in a stored procedure.

SQL SELECT statement, column names as values from another table

I'm working on a database which has the following table:
id location
1 Singapore
2 Vancouver
3 Egypt
4 Tibet
5 Crete
6 Monaco
My question is, how can I produce a query from this which would result in column names like the following without writing them into the query:
Query result:
Singapore , Vancouver, Egypt, Tibet, ...
< values >
how can I produce a query which would result in column names like the
following without writing them into the query:
Even with crosstab() (from the tablefunc extension), you have to spell out the column names.
Except, if you create a dedicated C function for your query. The tablefunc extension provides a framework for this, output columns (the list of countries) have to be stable, though. I wrote up a "tutorial" for a similar case a few days ago:
PostgreSQL row to columns
The alternative is to use CASE statements like this:
SELECT sum(CASE WHEN t.id = 1 THEN o.ct END) AS "Singapore"
, sum(CASE WHEN t.id = 2 THEN o.ct END) AS "Vancouver"
, sum(CASE WHEN t.id = 3 THEN o.ct END) AS "Egypt"
-- more?
FROM tbl t
JOIN (
SELECT id, count(*) AS ct
FROM other_tbl
GROUP BY id
) o USING (id);
ELSE NULL is optional in a CASE expression. The manual:
If the ELSE clause is omitted and no condition is true, the result is null.
Basics for both techniques:
PostgreSQL Crosstab Query
You could do this with some really messing dynamic sql but I wouldn't recommend it.
However you could produce something like below, let me know if that stucture is acceptable and I will post some sql.
Location | Count
---------+------
Singapore| 1
Vancouver| 0
Egypt | 2
Tibet | 1
Crete | 3
Monaco | 0
Script for SelectTopNRows command from SSMS
drop table #yourtable;
create table #yourtable(id int, location varchar(25));
insert into #yourtable values
('1','Singapore'),
('2','Vancouver'),
('3','Egypt'),
('4','Tibet'),
('5','Crete'),
('6','Monaco');
drop table #temp;
create table #temp( col1 int );
Declare #Script as Varchar(8000);
Declare #Script_prepare as Varchar(8000);
Set #Script_prepare = 'Alter table #temp Add [?] varchar(100);'
Set #Script = ''
Select
#Script = #Script + Replace(#Script_prepare, '?', [location])
From
#yourtable
Where
[id] is not null
Exec (#Script);
ALTER TABLE #temp DROP COLUMN col1 ;
select * from #temp;