I've pivoted a table and I have a base value, let's say 95%.
The schema is like
Name, BaseValue, Quarter1, Quarter2, etc..
West, 95% , 0.5% , -0.2% , ...
I'd like for Quarter 1 to be BaseValue+ Quarter1's initial value ie. 95.5%.
I'd like for Quarter 2 to be Quarter1 + Quarter2's initial value ie. 95.3%.
Here's the setup in SQLFiddle
http://sqlfiddle.com/#!3/78dd3/1
Unpivot, get the running totals, pivot back. Assuming the values are of a numeric type and the version used is SQL Server 2012, here's one way to implement that:
WITH UnpivotAndRunningTotals AS (
SELECT
Name,
Attr,
Value = SUM(Value) OVER (PARTITION BY Name ORDER BY Attr)
FROM atable
UNPIVOT (
Value FOR Attr IN (BaseValue, Quarter1, Quarter2, Quarter3, Quarter4)
) AS u
)
SELECT
Name,
BaseValue, Quarter1, Quarter2, Quarter3, Quarter4
FROM UnpivotAndRunningTotals
PIVOT (
MAX(Value) FOR Attr IN (BaseValue, Quarter1, Quarter2, Quarter3, Quarter4)
) AS p
;
With my comment above, I'll assume you want to do this and maintain the existing column values. The solution is a simple UPDATE using the + operator.
ALTER TABLE ADD [4Q13Total] FLOAT
UPDATE TABLE
SET [4Q13TOTAL] = [BASEOCC] + [4Q13]
You could also SELECT the values, if you'd like.
SELECT [BASEOCC] + [4Q13] AS [Q1Total]
Related
I need to calculate the difference of a column between two lines of a table. Is there any way I can do this directly in SQL? I'm using Microsoft SQL Server 2008.
I'm looking for something like this:
SELECT value - (previous.value) FROM table
Imagining that the "previous" variable reference the latest selected row. Of course with a select like that I will end up with n-1 rows selected in a table with n rows, that's not a probably, actually is exactly what I need.
Is that possible in some way?
Use the lag function:
SELECT value - lag(value) OVER (ORDER BY Id) FROM table
Sequences used for Ids can skip values, so Id-1 does not always work.
SQL has no built in notion of order, so you need to order by some column for this to be meaningful. Something like this:
select t1.value - t2.value from table t1, table t2
where t1.primaryKey = t2.primaryKey - 1
If you know how to order things but not how to get the previous value given the current one (EG, you want to order alphabetically) then I don't know of a way to do that in standard SQL, but most SQL implementations will have extensions to do it.
Here is a way for SQL server that works if you can order rows such that each one is distinct:
select rank() OVER (ORDER BY id) as 'Rank', value into temp1 from t
select t1.value - t2.value from temp1 t1, temp1 t2
where t1.Rank = t2.Rank - 1
drop table temp1
If you need to break ties, you can add as many columns as necessary to the ORDER BY.
WITH CTE AS (
SELECT
rownum = ROW_NUMBER() OVER (ORDER BY columns_to_order_by),
value
FROM table
)
SELECT
curr.value - prev.value
FROM CTE cur
INNER JOIN CTE prev on prev.rownum = cur.rownum - 1
Oracle, PostgreSQL, SQL Server and many more RDBMS engines have analytic functions called LAG and LEAD that do this very thing.
In SQL Server prior to 2012 you'd need to do the following:
SELECT value - (
SELECT TOP 1 value
FROM mytable m2
WHERE m2.col1 < m1.col1 OR (m2.col1 = m1.col1 AND m2.pk < m1.pk)
ORDER BY
col1, pk
)
FROM mytable m1
ORDER BY
col1, pk
, where COL1 is the column you are ordering by.
Having an index on (COL1, PK) will greatly improve this query.
LEFT JOIN the table to itself, with the join condition worked out so the row matched in the joined version of the table is one row previous, for your particular definition of "previous".
Update: At first I was thinking you would want to keep all rows, with NULLs for the condition where there was no previous row. Reading it again you just want that rows culled, so you should an inner join rather than a left join.
Update:
Newer versions of Sql Server also have the LAG and LEAD Windowing functions that can be used for this, too.
select t2.col from (
select col,MAX(ID) id from
(
select ROW_NUMBER() over(PARTITION by col order by col) id ,col from testtab t1) as t1
group by col) as t2
The selected answer will only work if there are no gaps in the sequence. However if you are using an autogenerated id, there are likely to be gaps in the sequence due to inserts that were rolled back.
This method should work if you have gaps
declare #temp (value int, primaryKey int, tempid int identity)
insert value, primarykey from mytable order by primarykey
select t1.value - t2.value from #temp t1
join #temp t2
on t1.tempid = t2.tempid - 1
Another way to refer to the previous row in an SQL query is to use a recursive common table expression (CTE):
CREATE TABLE t (counter INTEGER);
INSERT INTO t VALUES (1),(2),(3),(4),(5);
WITH cte(counter, previous, difference) AS (
-- Anchor query
SELECT MIN(counter), 0, MIN(counter)
FROM t
UNION ALL
-- Recursive query
SELECT t.counter, cte.counter, t.counter - cte.counter
FROM t JOIN cte ON cte.counter = t.counter - 1
)
SELECT counter, previous, difference
FROM cte
ORDER BY counter;
Result:
counter
previous
difference
1
0
1
2
1
1
3
2
1
4
3
1
5
4
1
The anchor query generates the first row of the common table expression cte where it sets cte.counter to column t.counter in the first row of table t, cte.previous to 0, and cte.difference to the first row of t.counter.
The recursive query joins each row of common table expression cte to the previous row of table t. In the recursive query, cte.counter refers to t.counter in each row of table t, cte.previous refers to cte.counter in the previous row of cte, and t.counter - cte.counter refers to the difference between these two columns.
Note that a recursive CTE is more flexible than the LAG and LEAD functions because a row can refer to any arbitrary result of a previous row. (A recursive function or process is one where the input of the process is the output of the previous iteration of that process, except the first input which is a constant.)
I tested this query at SQLite Online.
You can use the following funtion to get current row value and previous row value:
SELECT value,
min(value) over (order by id rows between 1 preceding and 1
preceding) as value_prev
FROM table
Then you can just select value - value_prev from that select and get your answer
Good day,
I have a sql table with the following setup:
DataPoints{ DateTime timeStampUtc , bit value}
The points are on a minute interval, and store either a 1(on) or a 0(off).
I need to write a stored procedure to find the points of interest from all the data points.
I have a simplified drawing below:
I need to find the corner points only. Please note that there may be many data points between a value change. For example:
{0,0,0,0,0,0,0,1,1,1,1,0,0,0}
This is my thinking atm (high level)
Select timeStampUtc, Value
From Data Points
Where Value before or value after differs by 1 or -1
I am struggling to convert this concept to sql, and I also have a feeling there is an more elegant mathematical solution that I am not aware off. This must be a common problem in electronics?
I have wrapped the table into a CTE. Then, I am joining every row in the CTE to the next row of itself. Also, I've added a condition that the consequent rows should differ in the value.
This would return you all rows where the value changes.
;WITH CTE AS(
SELECT ROW_NUMBER() OVER(ORDER BY TimeStampUTC) AS id, VALUE, TIMESTAMPUTC
FROM DataPoints
)
SELECT CTE.TimeStampUTC as "Time when the value changes", CTE.id, *
FROM CTE
INNER JOIN CTE as CTE2
ON CTE.id = CTE2.id + 1
AND CTE.Value != CTE2.Value
Here's a working fiddle: http://sqlfiddle.com/#!6/a0ddc/3
If I got it correct, you are looking for something like this:
with cte as (
select * from (values (1,0),(2,0),(3,1),(4,1),(5,0),(6,1),(7,0),(8,0),(9,1)) t(a,b)
)
select
min(a), b
from (
select
a, b, sum(c) over (order by a rows unbounded preceding) grp
from (
select
*, iif(b = lag(b) over (order by a), 0, 1) c
from
cte
) t
) t
group by b, grp
So I'm doing a data mining project for one of my classes. As part of it, I'm trying to apply Min Max Normalization to some of the data- which is the easy part. The hard part had been actually inserting the results of the queries into the table.
At first, I tried an INSERT INTO statement...
insert into dbo.CountsA([TotalCountMinMAx])
SELECT
1.00*(TotalCount-MinCount)/CountRange as TotalCountMinMax
FROM
(
SELECT
TotalCount,
MIN(TotalCount) OVER () AS MinCount,
MAX(TotalCount) OVER () - MIN(TotalCount) OVER () AS CountRange
FROM
dbo.CountsA
) X
The subquery itself works fine, but the moment I tried inserting the results into the table, it only inserted a number of null records. So instead of, say, updating ten entries in the TotalCountMinMAx column, it created ten additional records, and set all the columns to NULL.
After busting my head trying to figure that out, I tried using an UPDATE query instead.
update dbo.CountsA
set [TotalCountMinMAx]=(
SELECT
1.00*(TotalCount-MinCount)/CountRange as TotalCountMinMax
FROM
(
SELECT
TotalCount,
MIN(TotalCount) OVER () AS MinCount,
MAX(TotalCount) OVER () - MIN(TotalCount) OVER () AS CountRange
FROM
dbo.CountsA
) X)
This query failed to run entirely.
"Subquery returned more than 1 value. This is not permitted when the subquery follows =, !=, <, <= , >, >= or when the subquery is used as an expression."
At this point, short of digging out my old SQL book and basically relearning SQL from scratch (I am very, very rusty), I'm out of ideas for making either of these codes work.
In case 1, when you insert, data will add row below old data. Example, if your table like:
ID | Col
1 2
2 4
After you insert just only Col column values: 3,4
Your table like this:
ID | Col
1 2
2 4
NULL 3
NULL 4
In case 2:
IF you use sub-query to insert like:
UPDATE Your_Table
SET Col = (<sub-query>)
sub-query must return a single value.
You can add where clause make sub-query return a single value, like this:
UPDATE Your_Table
SET Col = (SELECT ... FROM Your_Table) AS A
WHERE Col_ID = A.Col_ID
The problem is you are not correlating your sub-query that's the reason to get Sub-query retuned more than one row error.
Try using CTE to update which is easy and more readable.
;WITH cte
AS (SELECT 1.00 * ( TotalCount - MinCount ) / CountRange AS TotalCountMinMax_N,
TotalCountMinMax
FROM (SELECT TotalCount,
TotalCountMinMax,
Min(TotalCount)
OVER () AS MinCount,
Max(TotalCount)
OVER () - Min(TotalCount)
OVER () AS CountRange
FROM dbo.CountsA) X)
UPDATE cte
SET TotalCountMinMax = TotalCountMinMax_N
I have a columns named id and item and there are stored values like:
id item
1 value
2 value
3 value
etc. There are 192 rows. These values are in the system in different places and I need to find concrete value in database to change it to the name I need.
Is there some posibility to add number to rows, for example value_01, value_02 etc.
I know how to do it in C language, but have no idea how to do it in sql server.
Edited:
#lad2025
In system I have columns, that names are stored in database.
Names are same, for example:
In app Apple I have table name Apple
In app Storage I also have table name Apple
I need to change app Storage columns name Apple to different, but I dont know, which of databasa Apple values it is, so I want to add identifiers to string, to find the right one. So I need to update database values, to see them in system.
SQLFiddleDemo
DECLARE #pad INT = 3;
SELECT
[id],
[item] = [item] + '_' + RIGHT(REPLICATE('0', #pad) + CAST([id] AS NVARCHAR(10)), #pad)
FROM your_table;
This will produce result like:
value_001
value_010
value_192
EDIT:
After reading your comments it is not clear what you want to achieve, but check:
SqlFiddleDemo2
DECLARE #pad INT = 3;
;WITH cte AS
(
SELECT *,
[rn] = ROW_NUMBER() OVER (PARTITION BY item ORDER BY item)
FROM your_table
)
SELECT
[id],
[item] = [item] + '_' + RIGHT(REPLICATE('0', #pad) + CAST([rn] AS NVARCHAR(10)), #pad)
FROM cte
WHERE item = 'value'; /* You can comment it if needed */
I've written a stored procedure that is returning a 2 column temp table, one ID column that is not unique, but a value between 2 and 12 to group values on. The other column is that actual data value column. I want to break out this 1 column table into a table into basically 1 table of 11 columns, 1 column for each dataset.
i'd like to have this parsed out into columns by ID. An identity column is not necessary since they will be unique to their own column. Something like;
Data2 Data3 Data4
102692... 103516.... 108408....
104114... 103476.... 108890....
and so on. I have tried using a While Loop through the datasets, but it's mainly getting these contained in 1 insert that is troubling me. I can't figure out how to say
While recordCount > 0
Begin
Insert into #tempTable(value1ID2,value1ID3,Value1ID4)
End
and then loop through value2ID2, value2ID3 etc.
If this isn't attainable that's fine i'll have to figure out a workaround, but the main reason i'm trying to do this is for a Report Builder dataset for a line chart that will eventually share a date grouping.
Since you need to aggregate string values, then you will need to use either the max or min aggregate function. The problem with that is it will return a single row for each column. In order to rerun multiple rows, then you will need to use a windowing function like row_number() to generate a unique value for each id/string combination. This will allow you to return multiple rows for each id:
select Data2 = [2], Data3 = [3], Data4 = [4]
from
(
select id, stringvalue,
row_number() over(partition by id order by stringvalue) seq
from yourtable
) d
pivot
(
max(stringvalue)
for id in ([2], [3], [4])
) piv