Script required - sql

Suppose we have a table #temp1, what we required here is, we want an additional column ABC and in that, we want to print the output as (10-10 = 0), (20-10) = 10, (30-10 = 20), (40-10 = 30) and (50-10 = 40)
So, we have created a table and insert script below.
Create table #temp1 (ID Int Identity(1,1),Name varchar(10),Series bigint)
insert into #temp1 values('A',10)
insert into #temp1 values('B',20)
insert into #temp1 values('C',30)
insert into #temp1 values('D',40)
insert into #temp1 values('E',50)
I tried as below, where its incrementing the values rows by rows.
select ID,Name,Series, SUM(series) over(order by series asc Rows Between Unbounded Preceding and Current Row) ranking from #temp1
Output should be:
ID|Name|Series|ABC
1 | A | 10 | 0
2 | B | 20 | 10
3 | C | 30 | 20
4 | D | 40 | 30
5 | E | 50 | 40
Can anyone here me, how to do that.

It's simply Series - 10:
SELECT ID, Name, Series, Series - 10 AS ABC
FROM #Temp1;

try the below query
select ID,Name,Series,(series - (select top 1 series from #temp1)) as abc from #temp1

Agree with Sami. Unless something else is intended from the query that was attempted, it seems like it should not be Series - 10, but rather series - the first series value in the table?
If that is the case, then the answer should be
select ID,Name,Series, Series - MIN(series) over
(order by series asc Rows Between unbounded Preceding and Current Row) ABC from #temp1

select ID,Name,Series,(series - 10) as abc from #temp1

Related

SQL select a row X times and insert into new

I am trying to migrate a bunch of data from an old database to a new one, the old one used to just have the number of alarms that occurred on a single row. The new database inserts a new record for each alarm that occurs. Here is a basic version of how it might look. I want to select each row from Table 1 and insert the number of alarm values as new rows into Table 2.
Table 1:
| Alarm ID | Alarm Value |
|--------------|----------------|
| 1 | 3 |
| 2 | 2 |
Should go into the alarm table as the below values.
Table 2:
| Alarm New ID | Value |
|--------------|----------|
| 1 | 1 |
| 2 | 1 |
| 3 | 1 |
| 4 | 2 |
| 5 | 2 |
I want to create a select insert script that will do this, so the select statement will bring back the number of rows that appear in the "Value" column.
A recursive CTE can be convenient for this:
with cte as (
select id, alarm, 1 as n
from t
union all
select id, alarm, n + 1
from cte
where n < alarm
)
select row_number() over (order by id) as alarm_id, id as value
from cte
order by 1
option (maxrecursion 0);
Note: If your values do not exceed 100, then you can remove OPTION (MAXRECURSION 0).
Replicate values out with a CTE.
DECLARE #T TABLE(AlarmID INT, Value INT)
INSERT #T VALUES
(1,3),
(2,2)
;WITH ReplicateAmount AS
(
SELECT AlarmID, Value FROM #T
UNION ALL
SELECT R.AlarmID, Value=(R.Value - 1)
FROM ReplicateAmount R
INNER JOIN #T T ON R.AlarmID = T.AlarmID
WHERE R.Value > 1
)
SELECT
AlarmID = ROW_NUMBER() OVER( ORDER BY AlarmID),
Value = AlarmID --??
FROM
ReplicateAmount
ORDER BY
AlarmID
This answers your question. I would think the query below would be more useful, however, you did not include usage context.
SELECT
AlarmID,
Value
FROM
ReplicateAmount
ORDER BY
AlarmID
Rather than using an rCTE, which is recursive (as the name suggests) and will fail at 100 rows, you can use a Tally table, which tend to be far faster as well:
WITH N AS(
SELECT N
FROM (VALUES(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL))N(N)),
Tally AS(
SELECT ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) AS I
FROM N N1, N N2, N N3)
SELECT ROW_NUMBER() OVER (ORDER BY V.AlarmID,T.I) AS AlarmNewID,
V.AlarmID
FROM (VALUES(1,3),(2,2))V(AlarmID,AlarmValue)
JOIN Tally T ON V.AlarmValue >= T.I;

How can I get column name based on row value in SQL Server?

I got an lookup table with a lot of columns with values.
How can I get this output from the input using Microsoft SQL Server?(Basically select the column name where Date = MAX() AND ColX to ColZ value =< 5.4).
Find the row with the latest date. MAX(Date)
Look in ColX, is my value 5.4 higher than ColX's value? Y/N
Yes. Look in ColY, is my value 5.4 higher than ColY's value? Y/N
Yes. Look in ColZ, is my value 5.4 higher than ColZ's value? Y/N
No. Output Column ColY AS Column
Input
ID Date ColX ColY ColZ
-----------------------------------------------------
79185673 2018-11-28 00:00:00 3 5 7
79185673 2018-12-02 00:00:00 2 4 6
79185673 2018-12-04 00:00:00 4 5 6
Output
ID Date Column
--------------------------------------
79185673 2018-12-04 00:00:00 ColY
Is this what are you looking for
SELECT TOP 1 *,
CASE WHEN 5.4 > ColZ
THEN 'ColZ'
WHEN 5.4 > ColY
THEN 'ColY'
WHEN 5.4 > ColX
THEN 'ColX'
END [Column]
FROM T
ORDER BY [Date] DESC;
Column names are fixed in the DDL are meta data, not values stored within the rows. You can view the column names by selecting from the information_schema.columns table. However, to implement the logic you are requesting can be done using a CASE statement in SQL.
Assuming the table name is table1, try the following query:
declare #compare_value decimal(2,1);
#compare_value = 5.4;
select
t.ID,
t.Date,
case when t.colX <= #compare_value then
case when t.ColY <= #compare_value then
case when t.ColZ <= #compare_value then
'ColZ'
else
'ColY'
end
else
'ColY'
end
else
'ColX'
end as "Column"
from table1 t
where t.date = (
select max(t1.date)
from table1 t1
where t1.ID = t.ID
);
A word of caution: Already the code is nested to 3 levels, and will need to be nested for each column (perhaps to 150 levels)! That is some serious spagetti code. It will work, but will look messy. If the data volume is huge then performance could also be an issue as SQL is not really that useful for complex logic.
You would be better off using SQL for just selecting the data you want and using a stored procedure or supplying it via an ODBC connection to say .NET or Python. Then do your complex logic processing there.
We'll take this in steps. Here's the data setup:
DECLARE #table TABLE
(
ID INTEGER NOT NULL
,Date DATETIME NOT NULL
,ColX INTEGER NOT NULL
,ColY INTEGER NOT NULL
,ColZ INTEGER NOT NULL
);
INSERT INTO #table
(ID,Date,ColX,ColY,ColZ)
VALUES
(79185673, '2018-11-28T00:00:00', 3, 5, 7);
INSERT INTO #table
(ID,Date,ColX,ColY,ColZ)
VALUES
(79185673, '2018-12-02T00:00:00', 2, 4, 6);
INSERT INTO #table
(ID,Date,ColX,ColY,ColZ)
VALUES
(79185673, '2018-12-04T00:00:00', 4, 5, 6);
First, we'll find the record with the maximum date.
SELECT TOP (1)
*
FROM
#table
ORDER BY
[Date] DESC
+----------+-------------------------+------+------+------+
| ID | Date | ColX | ColY | ColZ |
+----------+-------------------------+------+------+------+
| 79185673 | 2018-12-04 00:00:00.000 | 4 | 5 | 6 |
+----------+-------------------------+------+------+------+
So that forms our base data set. From there, we want to UNPIVOT to get all of the column values into a single column. You'll have to type out all of the other column names in the UNPIVOT, but you can probably get SSMS to do some of that scripting for you by just using a default SELECT TOP N ROWS query and copying and pasting the column names from there.
SELECT
*
FROM
(
SELECT
TOP (1)
*
FROM
#table
ORDER BY
[Date] DESC
) AS d
UNPIVOT
(
Nums
FOR ColName IN (ColX, ColY, ColZ)
) AS p
+----------+-------------------------+------+---------+
| ID | Date | Nums | ColName |
+----------+-------------------------+------+---------+
| 79185673 | 2018-12-04 00:00:00.000 | 4 | ColX |
| 79185673 | 2018-12-04 00:00:00.000 | 5 | ColY |
| 79185673 | 2018-12-04 00:00:00.000 | 6 | ColZ |
+----------+-------------------------+------+---------+
According to the comments, the numbers are always increasing across the columns, so we can sort by them safely and maintain the original order. But we only care about numbers that are smaller than the target number, 5.4 in this example. So that's our WHERE clause. And we want the largest number that's less than 5.4, so we'll be using a descending ORDER BY clause. We only want that single value, so we only need the TOP (1) in our final results.
DECLARE #target DECIMAL(5,1) = 5.4;
SELECT TOP (1)
*
FROM
(
SELECT
TOP (1)
*
FROM
#table
ORDER BY
[Date] DESC
) AS d
UNPIVOT
(
Nums
FOR ColName IN (ColX, ColY, ColZ)
) AS p
WHERE
p.Nums < #target
ORDER BY
p.Nums DESC;
+----------+-------------------------+------+---------+
| ID | Date | Nums | ColName |
+----------+-------------------------+------+---------+
| 79185673 | 2018-12-04 00:00:00.000 | 5 | ColY |
+----------+-------------------------+------+---------+
I doubt this is what the OP is after, based on their comments, but based on their question, this answer would be:
DECLARE #MyValue decimal(2,1) = 5.4
WITH CTE AS(
SELECT ID,
[Date],
ColX,Coly,ColZ,
ROW_NUMBER() OVER (ORDER BY [Date] DESC) AS RN --PARTITION BY ID?
FROM TheirTable)
SELECT ID,
[Date],
CASE WHEN ColZ < #MyValue THEN 'ColZ'
WHEN ColY < #MyValue THEN 'ColY'
WHEN ColX < #MyValue THEN 'ColX'
END AS [Column]
FROM CTE
WHERE RN = 1;
Their comment (under the question) is somewhat leading, but after updating their question they still only have 5 columns, so I'm going to assume that 5 is what they really have. With no real definitive explanation either, this my my "best guess".

get the nth-lowest value in a `group by` clause

Here's a tough one: I have data coming back in a temporary table foo in this form:
id n v
-- - -
1 3 1
1 3 10
1 3 100
1 3 201
1 3 300
2 1 13
2 1 21
2 1 300
4 2 1
4 2 7
4 2 19
4 2 21
4 2 300
8 1 11
Grouping by id, I need to get the row with the nth-lowest value for v based on the value in n. For example, for the group with an ID of 1, I need to get the row which has v equal to 100, since 100 is the third-lowest value for v.
Here's what the final results need to look like:
id n v
-- - -
1 3 100
2 1 13
4 2 7
8 1 11
Some notes about the data:
the number of rows for each ID may vary
n will always be the same for every row with a given ID
n for a given ID will never be greater than the number of rows with that ID
the data will already be sorted by id, then v
Bonus points if you can do it in generic SQL instead of oracle-specific stuff, but that's not a requirement (I suspect that rownum may factor prominently in any solutions). It has in my attempts, but I wind up confusing myself before I get a working solution.
I would use row_number function make row number the compare with n column value in CTE, do another CTE to make row number order by v desc.
get rn = 1 which is mean max value in the n number group.
CREATE TABLE foo(
id int,
n int,
v int
);
insert into foo values (1,3,1);
insert into foo values (1,3,10);
insert into foo values (1,3,100);
insert into foo values (1,3,201);
insert into foo values (1,3,300);
insert into foo values (2,1,13);
insert into foo values (2,1,21);
insert into foo values (2,1,300);
insert into foo values (4,2,1);
insert into foo values (4,2,7);
insert into foo values (4,2,19);
insert into foo values (4,2,21);
insert into foo values (4,2,300);
insert into foo values (8,1,11);
Query 1:
with cte as(
select id,n,v
from
(
select t.*, row_number() over(partition by id ,n order by n) as rn
from foo t
) t1
where rn <= n
), maxcte as (
select id,n,v, row_number() over(partition by id ,n order by v desc) rn
from cte
)
select id,n,v
from maxcte
where rn = 1
Results:
| ID | N | V |
|----|---|-----|
| 1 | 3 | 100 |
| 2 | 1 | 13 |
| 4 | 2 | 7 |
| 8 | 1 | 11 |
use window function
select * from
(
select t.*, row_number() over(partition by id ,n order by v) as rn
from foo t
) t1
where t1.rn=t1.n
as ops sample output just need 3rd highest value so i put where condition t1.rn=3 though accodring to description it would be t1.rn=t1.n
https://dbfiddle.uk/?rdbms=oracle_11.2&fiddle=65abf8d4101d2d1802c1a05ed82c9064
If your database is version 12.1 or higher then there is a much simpler solution:
SELECT DISTINCT ID, n, NTH_VALUE(v,n) OVER (PARTITION BY ID) AS v
FROM foo
ORDER BY ID;
| ID | N | V |
|----|---|-----|
| 1 | 3 | 100 |
| 2 | 1 | 13 |
| 4 | 2 | 7 |
| 8 | 1 | 11 |
Depending on your real data you may have to add an ORDER BY n clause and/or windowing_clause as RANGE BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING, see NTH_VALUE

SQL SELECT Convert Min/Max into Separate Rows

I have a table that has a min and max value that I'd like create a row for each valid number in a SELECT statement.
Original table:
| Foobar_ID | Min_Period | Max_Period |
---------------------------------------
| 1 | 0 | 2 |
| 2 | 1 | 4 |
I'd like to turn that into:
| Foobar_ID | Period_Num |
--------------------------
| 1 | 0 |
| 1 | 1 |
| 1 | 2 |
| 2 | 1 |
| 2 | 2 |
| 2 | 3 |
| 2 | 4 |
The SELECT results need to come out as one result-set, so I'm not sure if a WHILE loop would work in my case.
If you expect just a handful of rows per foobar, then this is a good opportunity to learn about recursive CTEs:
with cte as (
select foobar_id, min_period as period_num, max_period
from original t
union all
select foobar_id, min_period + 1 as period_num, max_period
from cte
where period_num < max_period
)
select foobar_id, period_num
from cte
order by foobar_id, period_num;
You can extend this to any number of periods by setting the MAXRECURSION option to 0.
One method would be to use a Tally table, ther's plenty of examples out there, but I'm going to create a very small one in this example. Then you can JOIN onto that and return your result set.
--Create the Tally Table
CREATE TABLE #Tally (I int);
WITH ints AS(
SELECT 0 AS i
UNION ALL
SELECT i + 1
FROM ints
WHERE i + 1 <= 10)
--And in the numbers go!
INSERT INTO #Tally
SELECT i
FROM ints;
GO
--Create the sample table
CREATE TABLE #Sample (ID int IDENTITY(1,1),
MinP int,
MaxP int);
--Sample data
INSERT INTO #Sample (Minp, MaxP)
VALUES (0,2),
(1,4);
GO
--And the solution
SELECT S.ID,
T.I AS P
FROM #Sample S
JOIN #Tally T ON T.I BETWEEN S.MinP AND S.MaxP
ORDER BY S.ID, T.I;
GO
--Clean up
DROP TABLE #Sample;
DROP TABLE #Tally;
Depending on the size of the data and the range of the period, the easiest way to do this is to use a dynamic number fact table, as follows:
WITH rn AS (SELECT ROW_NUMBER() OVER (ORDER BY object_id) -1 as period_num FROM sys.objects)
SELECT f.foobar_id, rn.period_num
FROM foobar f
INNER JOIN rn ON rn.period_num BETWEEN f.min_period AND f.max_period
However, if you're working with a larger volume of data, it will be worth creating a number fact table with an index. You can even use a TVV for this:
-- Declare the number fact table
DECLARE #rn TABLE (period_num INT IDENTITY(0, 1) primary key, dummy int)
-- Populate the fact table so that all periods are covered
WHILE (SELECT COUNT(1) FROM #rn) < (SELECT MAX(max_period) FROM foobar)
INSERT #rn select 1 from sys.objects
-- Select using a join to the fact table
SELECT f.foo_id, rn.period_num
FROM foobar f
inner join #rn rn on rn.period_num between f.min_period and f.max_period
Just Create a function sample date and use it
CREATE FUNCTION [dbo].[Ufn_GetMInToMaxVal] (#Min_Period INT,#Max_Period INT )
RETURNS #OutTable TABLE
(
DATA INT
)
AS
BEGIN
;WIth cte
AS
(
SELECT #Min_Period As Min_Period
UNION ALL
SELECT Min_Period+1 FRom
cte
WHERE Min_Period < #Max_Period
)
INSERT INTO #OutTable
SELECT * FROM cte
RETURN
END
Get the result by executing sql statement
DECLARE #Temp AS TABLE(
Foobar_ID INT,
Min_Period INT,
Max_Period INT
)
INSERT INTO #Temp
SELECT 1, 0,2 UNION ALL
SELECT 2, 1,4
SELECT Foobar_ID ,
DATA
FROM #Temp
CROSS APPLY
[dbo].[Ufn_GetMInToMaxVal] (Min_Period,Max_Period)
Result
Foobar_ID DATA
----------------
1 0
1 1
1 2
2 1
2 2
2 3
2 4

Juggling the values of a column in oracle

In a table tab I have a column with the name of col1 and it has 5 rows with values 1 to 5.
col1
1
2
3
4
5
Now I want to write a select query which will juggle the values in col1,distribute it and put those values in new column.
Below output will help you understand my requirement.
col1 New_col
1 3
2 5
3 4
4 1
5 2
Note: If 1 is changed to 3, then no other value in col1 after juggling should result in 3. i have to do it for 500 rows, i am taking a small example for better understanding.
Please let me know if you require further clarification.
This is a step by step approach:
Try it at SQL Fiddle
Oracle 11g R2 Schema Setup:
create table t ( i int );
insert into t values (1);
insert into t values (2);
insert into t values (3);
insert into t values (4);
insert into t values (5);
Step by step query:
with
/*add a random column to shuffle*/
a as
( select i, dbms_random.value as o
from t),
/*get last element to pair it with the first*/
b as
( select i,
o,
last_Value(i) over (ORDER BY o asc
ROWS BETWEEN UNBOUNDED PRECEDING
AND UNBOUNDED FOLLOWING) AS i2
from a)
/*pair each element with the next one, take the last one as default*/
select i, LAG(i, 1, i2 ) OVER (ORDER BY o ) AS i3
from b
Results:
| I | I3 |
|---|----|
| 2 | 5 |
| 1 | 2 |
| 3 | 1 |
| 4 | 3 |
| 5 | 4 |
What about this?
SELECT row_number() over (order by 1) col, col1 new_col
FROM tab
ORDER BY DBMS_RANDOM.VALUE
demo