TSQL Select all columns without first n from function/procedure - sql

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.

Related

How to use variable and changing user defined functions in query

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

update comma separated values in a column

Before we begin, this is a bad situation / design, but I am not the able to fix the design as the column is used by an application and database that I ( my company ) don't own. I do not control the new or old values either.
I have a table similar to this:
Create Table #tblCRigrationTest
(
ReasonString VarChar(1000),
ReasonStringNew VarChar(1000)
)
With data like this:
Insert Into #tblCRigrationTest ( ReasonString, ReasonStringNew )
Values ('5016|5005|5006|5032|5020|5010|5007|5011|5012|5028|5024|5008|5029', '')
What I need to do is "loop" through each ID and based on its value, update it, concatenate it into a new string, and then store it in the ReasonStringNew column. The new ID's appear in the second column below:
Old New
--------------
5005 1
5006 2
5020 3
5032 4
5010 5
5007 6
5011 7
5012 8
5028 9
5024 10
5008 11
5016 12
5009 13
5029 14
Any suggestions on how to do this?
just take your column values in a temp table then try to update
SET #STRSQL = 'SELECT ''' + REPLACE('Yourcolumn, ',',
''' ,(your saparator)''') + ''''
DECLARE #tbl TABLE
(
col1 VARCHAR(100) ,
)
INSERT INTO #tbl
EXECUTE ( #STRSQL
)

How to find Running Multiplication

Not sure is this the right title. I need to find the cumulative multiplication as like running total.
Searched the forum and got a excellent answer. But it is not the exact answer for me.
so modified the answer to my requirement.
SELECT *,
(SELECT CASE
WHEN Min(Abs(Column1)) = 0 THEN 0
ELSE Exp(Sum(Log(Abs(NULLIF(Column1, 0))))) -- the base mathematics
* Round(0.5 - Count(NULLIF(Sign(Sign(Column1) + 0.5), 1))%2, 0) -- pairs up negatives
END
FROM TEMP a
WHERE B.ID >= A.ID) as Running_Mul
FROM TEMP B
And I got my answer. Now Is there any better way of doing this in Sql Server 2008?
Sample data:
ID Column1
-- -------
1 1
2 2
3 4
4 8
5 -2
Expected Result:
ID Column1 Running_Mul
-- ------- -----------
1 1 1
2 2 2
3 4 8
4 8 64
5 -2 -128
Sql Fiddle
Your method is pretty reasonable. Good catch on the nullif() in the sum(), by the way. Although the else clause is computed only after the then, components of the else are calculated during the aggregation -- so log(0) would return an error.
I think there are some simpler ways to calculate the sign, such as:
power(-1, sum(case when column1 < 0 then 1 else 0 end))
or:
(case when sum(case when column1 < 0 then 1 else 0 end) % 2 = 0 then 1 else -1 end)
However, which version is "simpler" is a matter of opinion.
Here is another approach which I use in my SPs :
USE DB
GO
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED
GO
IF(OBJECT_ID('TEMP') IS NOT NULL)
DROP TABLE TEMP
CREATE TABLE TEMP (ID INT, Column1 INT)
INSERT INTO TEMP VALUES
(1,1),
(2,2),
(3,4),
(4,8),
(5,-2)
DECLARE #result TABLE(ID INT, Column1 INT, calc INT)
DECLARE #Calc INT = 1
INSERT INTO #result (ID,Column1)
SELECT ID,Column1 FROM TEMP ORDER BY ID
UPDATE #result SET #Calc = calc = Column1 * #Calc
SELECT * FROM #result
I found a blog in which different methods to solve such problem, have been compared. check here.

SQL column sorting by value

Using T-SQL I'm creating a temp table grid. I need to reorder the columns based on the total of the column starting with the largest.
For example
---- DO MO BC NI SC
Total 22 44 53 57 24
Prod A 0 24 0 24 0
Prod B 0 0 0 20 7
Prod C 0 20 0 13 13
Would become:
---- NI BC MO SC DO
Total 57 53 44 24 22
Prod A 24 0 24 0 0
Prod B 20 0 0 7 0
Prod C 13 0 20 13 0
First of, ---- if a terrible column name but I could think of no better for this so I kept it.
You can build the query dynamically where you sort the columns when you build the query string.
declare #SQL nvarchar(max)
set #SQL = '
select [----]'+
(
select ', '+T2.N.value('local-name(.)', 'nvarchar(128)')
from (
select DO, MO, BC, NI, SC
from T
where [----] = 'Total'
for xml path(''), type
) as T1(X)
cross apply T1.X.nodes('*') as T2(N)
order by T2.N.value('.', 'int') desc
for xml path('')
)+'
from T'
exec (#SQL)
SQL Fiddle
Update
If you think the XML version of building the dynamic query is a bit complicated and unintuitive you can use this instead, totally void of XML stuff.
declare #SQL nvarchar(max)
declare #Col nvarchar(128)
declare C cursor local fast_forward for
select U.Col
from (
select DO, MO, BC, NI, SC
from T
where [----] = 'Total'
) as T
unpivot(Val for Col in (DO, MO, BC, NI, SC)) as U
order by U.Val desc
set #SQL = 'select [----]'
open C
fetch next from C into #Col
while ##FETCH_STATUS = 0
begin
set #SQL = #SQL + ',' + #Col
fetch next from C into #Col
end
close C
deallocate C
set #SQL = #SQL + ' from T'
exec (#SQL)
SQL Fiddle
If is is a temp table you could:
create it
calculate the column order
create another table with the correct order
insert into that table (in the correct order)
But I still have to ask why?
You do know you can order the columns in a select statement?
Do do know how to sum the columns?
select sum(col1), sum(col2)
from #temp
Unfortunately you cannot reorder columns in SQL.

get specific rows of table given a rule SQL Server 2008

I have a table like:
ID NAME VAL
----------------------
1 a1*a1 90052
2 a1*a2 236
3 a1*a3 56
4 a1*a4 6072
5 a1*a5 1004
6 a2*a2 4576
7 a2*a3 724
8 a2*a4 230
9 a2*a5 679
10 a3*a3 5
11 a3*a4 644
12 a3*a5 23423
13 a4*a4 42354
14 a4*a5 10199
15 a5*a5 10279
Given a number given S = 5, I want to query
the rows wth id: 1,6,10,13,15
they are a1*a1,a2*a2,a3*a3,a4*a4 and a5*a5
I would like something like:
INSERT #NEW_TABLE (ID,NAME,Value) (
SELECT ordinal, NAME, VAL FROM myTable where id = 1,6,10,13,15)
to get
ID NAME VAL
----------------------
1 a1*a1 90052
2 a2*a2 4576
3 a3*a3 5
4 a4*a4 42354
5 a5*a5 10279
Is there a way to do this for any given S, Maybe wth dynamic sql?
I was getting the formula and I got this:
S=5
ID formula
1 1
6 1+S
10 1+S+ (S-1)
13 1+S+ (S-1) + (S-2)
15 1+S+ (S-1) + (S-2) + (S-3)
Is there a way to do this inside a case or a while loop?
This worked in testing.
You can just inner join on #Tab to limit your results. You probably also want to add some traps for values below 3, which I haven't done.
The basic process is
Declare your #s value
Insert the first two rows since they will always be the same
In a loop, insert one row at a time with an incrementing difference
Loop exits once it has run #s-2 times
Try:
DECLARE #Tab Table (id INT)
DECLARE #S int = 5,
#ct int
DECLARE #cur int = (1 + #S)
INSERT INTO #Tab SELECT 1
INSERT INTO #Tab SELECT (1 + #S)
SET #ct = 1
WHILE #ct <= #S - 2
BEGIN
SET #cur = #cur + (#S - #ct)
INSERT INTO #Tab SELECT #cur
SET #ct = #ct + 1
END
SELECT * FROM #Tab
ORDER BY id
To use this in your query, you can do either:
SELECT ordinal, NAME, VAL
FROM myTable
WHERE id IN (SELECT id FROM #Tab)
-- OR
SELECT ordinal, NAME, VAL
FROM myTable t
INNER JOIN #tab t2
ON t2.id = t.id