adding a value to a column from data in next row sql - sql

Base Table
id line_number
1 1232
2 1456
3 1832
4 2002
I wish to add values to a new table such that the next row's value becomes the value in a new column with the last row's value being same..
The final output I need to produce is:
id line_number end_line_number
1 1232 1456
2 1456 1832
3 1832 2002
4 2002 2002
The database is sql server.
Any help is sincerely appreciated.
Thanks

After SQL Server 2012, you can use LEAD like this.
;WITH BaseTable as
(
SELECT 1 id, 1232 line_number
UNION ALL SELECT 2 , 1456
UNION ALL SELECT 3, 1832
UNION ALL SELECT 4 , 2002
)
SELECT id,line_number,(LEAD(line_number,1,line_number) OVER(ORDER BY id ASC))
FROM BaseTable
For previous versions, try this
;WITH BaseTable as
(
SELECT 1 id, 1232 line_number
UNION ALL SELECT 2 , 1456
UNION ALL SELECT 3, 1832
UNION ALL SELECT 4 , 2002
), OrderedBaseTable as
(
SELECT id,line_number,ROW_NUMBER() OVER(ORDER BY id asc) rw
FROM BaseTable
)
SELECT t1.id,t1.line_number,ISNULL(t2.line_number,t1.line_number) next_line_number
FROM OrderedBaseTable t1
LEFT JOIN OrderedBaseTable t2
ON t1.rw = t2.rw - 1

Try this
With T as (
Select id, line_number, Row_Number() OVER(Order By id) + 1 As TempId From TableName)
Select T1.id, T1.line_number, ISNULL(T2.line_number,T1.line_number) As end_line_number From T T1
Left Join T T2 on T2.id = T1.TempId
SQL Fiddle Demo

Related

SQL Grouping by first digit from sets of record

I need your help in SQL
I have a set of records of Cost center ID below.
what I want to do is to segregate/group them by inserting column to distinguish the category.
as you can see all digits start in 7 is belong to the bold digits.
my expected out is on below image also.
You can as the below:
DECLARE #Tbl TABLE (ID INT)
INSERT INTO #Tbl
VALUES
(735121201),
(735120001),
(5442244),
(735141094),
(735141097),
(4008060),
(735117603),
(40100000),
(735142902),
(735151199),
(4010070)
;WITH TableWithRowId
AS
(
SELECT
ROW_NUMBER() OVER (ORDER BY(SELECT NULL)) RowId,
ID
FROM
#Tbl
), TempTable
AS
(
SELECT T.RowId + 1 AS RowId FROM TableWithRowId T
WHERE
LEFT(T.ID, 1) != 7
), ResultTable
AS
(
SELECT
T.RowId ,
T.ID,
DENSE_RANK() OVER (ORDER BY (SELECT TOP 1 A.RowId FROM TempTable A WHERE A.RowId > T.RowId ORDER BY A.RowId)) AS Flag
FROM TableWithRowId T
)
SELECT * FROM ResultTable
Result:
RowId ID Flag
----------- ----------- ----------
1 735121201 1
2 735120001 1
3 5442244 1
4 735141094 2
5 735141097 2
6 4008060 2
7 735117603 3
8 40100000 3
9 735142902 4
10 735151199 4
11 4010070 4
The following query is similer with NEER's
;WITH test_table(CenterID)AS(
SELECT '735121201' UNION ALL
SELECT '735120001' UNION ALL
SELECT '5442244' UNION ALL
SELECT '735141094' UNION ALL
SELECT '735141097' UNION ALL
SELECT '4008060' UNION ALL
SELECT '735117603' UNION ALL
SELECT '40100000' UNION ALL
SELECT '735142902' UNION ALL
SELECT '735151199' UNION ALL
SELECT '4010070'
),t1 AS (
SELECT *,ROW_NUMBER()OVER(ORDER BY(SELECT 1)) AS rn,CASE WHEN LEFT(t.CenterID,1)='7' THEN 1 ELSE 0 END AS isSeven
FROM test_table AS t
),t2 AS(
SELECT t1.*,ROW_NUMBER()OVER(ORDER BY t1.rn) AS toFilter
FROM t1 LEFT JOIN t1 AS pt ON pt.rn=t1.rn-1
WHERE pt.CenterID IS NULL OR (t1.isSeven=1 AND pt.isSeven=0)
)
SELECT t1.CenterID,x.toFilter FROM t1
CROSS APPLY(SELECT TOP 1 t2.toFilter FROM t2 WHERE t2.rn<=t1.rn ORDER BY rn desc) x
CenterID toFilter
--------- --------------------
735121201 1
735120001 1
5442244 1
735141094 2
735141097 2
4008060 2
735117603 3
40100000 3
735142902 4
735151199 4
4010070 4

Oracle sql group sum

I have table With ID,Sub_ID and value coloumns
ID SUB_ID Value
100 1 100
100 2 150
101 1 100
101 2 150
101 3 200
102 1 100
SUB ID can vary from 1..maxvalue( In this example it is 3). I need Sum of values for each Sub_ID. If SUB_ID is less than MAXVALUE for a particlaur ID then it should take MAX(SUB_ID) of each ID As shown below ( In this example for ID=100 for SUB_ID 3 it should take 150 i.e 2<3 so value=150))
SUB_ID SUM(values) Remarks
1 300 (100+100+100)
2 400 (150+150+100)
3 450 (150+200+100)
This can be easily done in PL/SQL . Can we use SQL for the same using Model Clause or any other options
SQL Fiddle
Oracle 11g R2 Schema Setup:
CREATE TABLE TableA ( ID, SUB_ID, Value ) AS
SELECT 100, 1, 100 FROM DUAL
UNION ALL SELECT 100, 2, 150 FROM DUAL
UNION ALL SELECT 101, 1, 100 FROM DUAL
UNION ALL SELECT 101, 2, 150 FROM DUAL
UNION ALL SELECT 101, 3, 200 FROM DUAL
UNION ALL SELECT 102, 1, 100 FROM DUAL
Query 1:
WITH sub_ids AS (
SELECT LEVEL AS sub_id
FROM DUAL
CONNECT BY LEVEL <= ( SELECT MAX( SUB_ID ) FROM TableA )
),
max_values AS (
SELECT ID,
MAX( VALUE ) AS max_value
FROM TableA
GROUP BY ID
)
SELECT s.SUB_ID,
SUM( COALESCE( a.VALUE, m.max_value ) ) AS total_value
FROM sub_ids s
CROSS JOIN
max_values m
LEFT OUTER JOIN
TableA a
ON ( s.SUB_ID = a.SUB_ID AND m.ID = a.ID )
GROUP BY
s.SUB_ID
Results:
| SUB_ID | TOTAL_VALUE |
|--------|-------------|
| 1 | 300 |
| 2 | 400 |
| 3 | 450 |
Try this
SELECT SUB_ID,SUM(values),
(SELECT DISTINCT SUBSTRING(
(
SELECT '+'+ CAST(values AS VARCHAR)
FROM table_Name AS T2
WHERE T2.SUB_ID = d.SUB_ID
FOR XML PATH ('')
),2,100000)[values]) as values
FROm table_Name d
GROUP BY SUB_ID
How about something like this:
select max_vals.sub_id, sum(nvl(table_vals.value,max_vals.max_value)) as sum_values
from (
select all_subs.sub_id, t1.id, max(t1.value) as max_value
from your_table t1
cross join (select sub_id from your_table) all_subs
group by all_subs.sub_id, t1.id
) max_vals
left outer join your_table table_vals
on max_vals.id = table_vals.id
and max_vals.sub_id = table_vals.sub_id
group by max_vals.sub_id;
The inner query gets you a list of all sub_id/id combinations and their fall-back values. The out query uses an nvl to use the table value if it exists and the fall-back value if it doesn't.

Group by values that are in sequence

I have some table like this
row chequeNo
1 15
2 19
3 20
4 35
5 16
and I need to get the result like this
row from to
1 15 16
2 19 20
3 35 35
so I need groups of chequeNo where values would be sequential without any interruptions. chequeNo is unique column. Additionally it should be done with one sql select query, because I haven't permissions to create any sql structures except select queries.
So is it possible?
Would be grateful for any help
You can use Aketi Jyuuzou's technique called Tabibitosan here:
SQL> create table mytable (id,chequeno)
2 as
3 select 1, 15 from dual union all
4 select 2, 19 from dual union all
5 select 3, 20 from dual union all
6 select 4, 35 from dual union all
7 select 5, 16 from dual
8 /
Table created.
SQL> with tabibitosan as
2 ( select chequeno
3 , chequeno - row_number() over (order by chequeno) grp
4 from mytable
5 )
6 select row_number() over (order by grp) "row"
7 , min(chequeno) "from"
8 , max(chequeno) "to"
9 from tabibitosan
10 group by grp
11 /
row from to
---------- ---------- ----------
1 15 16
2 19 20
3 35 35
3 rows selected.
Regards,
Rob.
This should work with Oracle 10 (only tested with Oracle 11)
select group_nr + 1,
min(chequeno) as start_value,
max(chequeno) as end_value
from (
select chequeno,
sum(group_change_flag) over (order by rn) as group_nr
from (
select row_number() over (order by chequeno) as rn,
chequeno,
case
when chequeno - lag(chequeno,1,chequeno) over (order by chequeno) <= 1 then 0
else 1
end as group_change_flag
from foo
) t1
) t2
group by group_nr
order by group_nr
(it should work with any DBMS supporting standard SQL windowing functions, e.g. PostgreSQL, DB2, SQL Server 2012)
Here is a "plain vanilla" approach:
SELECT T1.chequeNo, T2.chequeNo
FROM Table1 AS T1 INNER JOIN Table1 AS T2 ON T2.chequeNo >= T1.chequeNo
WHERE
NOT EXISTS (SELECT T0.chequeNo FROM Table1 T0 WHERE T0.chequeNo IN ((T1.chequeNo-1), (T2.chequeNo+1)))
AND (SELECT COUNT(*) FROM Table1 T0 WHERE T0.chequeNo BETWEEN T1.chequeNo AND T2.chequeNo)=(T2.chequeNo - T1.chequeNo + 1)
ORDER BY 1,2
Please let me know if it's too inefficient for large data sets.
CREATE TABLE YOUR_TABLE (
chequeNo NUMBER PRIMARY KEY
);
INSERT INTO YOUR_TABLE VALUES (15);
INSERT INTO YOUR_TABLE VALUES (19);
INSERT INTO YOUR_TABLE VALUES (20);
INSERT INTO YOUR_TABLE VALUES (35);
INSERT INTO YOUR_TABLE VALUES (16);
SELECT T1.chequeNo "from", T2.chequeNo "to"
FROM
(
SELECT chequeNo, ROW_NUMBER() OVER (ORDER BY chequeNo) RN
FROM (
SELECT chequeNo, LAG(chequeNo) OVER (ORDER BY chequeNo) PREV
FROM YOUR_TABLE
)
WHERE PREV IS NULL OR chequeNo > PREV + 1
) T1
JOIN
(
SELECT chequeNo, ROW_NUMBER() OVER (ORDER BY chequeNo) RN
FROM (
SELECT chequeNo, LEAD(chequeNo) OVER (ORDER BY chequeNo) NEXT
FROM YOUR_TABLE
)
WHERE NEXT IS NULL OR chequeNo < NEXT - 1
) T2
USING (RN);
Result:
from to
---------------------- ----------------------
15 16
19 20
35 35
If we spice things up a little...
INSERT INTO YOUR_TABLE VALUES (17);
INSERT INTO YOUR_TABLE VALUES (18);
...we get:
from to
---------------------- ----------------------
15 20
35 35

In Oracle, how do I get a page of distinct values from sorted results?

I have 2 columns in a one-to-many relationship. I want to sort on the "many" and return the first occurrence of the "one". I need to page through the data so, for example, I need to be able to get the 3rd group of 10 unique "one" values.
I have a query like this:
SELECT id, name
FROM table1
INNER JOIN table2 ON table2.fkid = table1.id
ORDER BY name, id;
There can be multiple rows in table2 for each row in table1.
The results of my query look like this:
id | name
----------------
2 | apple
23 | banana
77 | cranberry
23 | dark chocolate
8 | egg
2 | yak
19 | zebra
I need to page through the result set with each page containing n unique ids. For example, if start=1 and n=4 I want to get back
2
23
77
8
in the order they were sorted on (i.e., name), where id is returned in the position of its first occurrence. Likewise if start=3 and n=4 and order = desc I want
8
23
77
2
I tried this:
SELECT * FROM (
SELECT id, ROWNUM rnum FROM (
SELECT DISTINCT id FROM (
SELECT id, name
FROM table1
INNER JOIN table2 ON table2.fkid = table1.id
ORDER BY name, id)
WHERE ROWNUM <= 4)
WHERE rnum >=1)
which gave me the ids in numerical order, instead of being ordered as the names would be.
I also tried:
SELECT * FROM (
SELECT DISTINCT id, ROWNUM rnum FROM (
SELECT id FROM (
SELECT id, name
FROM table1
INNER JOIN table2 ON table2.fkid = table1.id
ORDER BY name, id)
WHERE ROWNUM <= 4)
WHERE rnum >=1)
but that gave me duplicate values.
How can I page through the results of this data? I just need the ids, nothing from the "many" table.
update
I suppose I'm getting closer with changing my inner query to
SELECT id, name, rank() over (order by name, id)
FROM table1
INNER JOIN table2 ON table2.fkid = table1.id
...but I'm still getting duplicate ids.
You may need to debug it a little, but but it will be something like this:
SELECT * FROM (
SELECT * FROM (
SELECT id FROM (
SELECT id, name, row_number() over (partition by id order by name) rn
FROM table1
INNER JOIN table2 ON table2.fkid = table1.id
)
) WHERE rn=1 ORDER BY name, id
) WHERE rownum>=1 and rownum<=4;
It's a bit convoluted (and I would tend to suspect that it could be simplified) but it should work. You'd can put whatever start and end position you want in the WHERE clause-- I'm showing here with start=2 and n=4 are pulled from a separate table but you could simplify things by using a couple of parameters instead.
SQL> ed
Wrote file afiedt.buf
1 with t as (
2 select 2 id, 'apple' name from dual union all
3 select 23, 'banana' from dual union all
4 select 77, 'cranberry' from dual union all
5 select 23, 'dark chocolate' from dual union all
6 select 8, 'egg' from dual union all
7 select 2, 'yak' from dual union all
8 select 19, 'zebra' from dual
9 ),
10 x as (
11 select 2 start_pos, 4 n from dual
12 )
13 select *
14 from (
15 select distinct
16 id,
17 dense_rank() over (order by min_id_rnk) outer_rnk
18 from (
19 select id,
20 min(rnk) over (partition by id) min_id_rnk
21 from (
22 select id,
23 name,
24 rank() over (order by name) rnk
25 from t
26 )
27 )
28 )
29 where outer_rnk between (select start_pos from x) and (select start_pos+n-1 from x)
30* order by outer_rnk
SQL> /
ID OUTER_RNK
---------- ----------
23 2
77 3
8 4
19 5

tsql - Setting sequential values without looping/cursoring

I need to set a non-unique identifier in a data table. This would be sequential within a group ie. for each group, the ID should start at 1 and rise in incremements of 1 until the last row for that group.
This is illustrated by the table below. "New ID" is the column I need to populate.
Unique ID Group ID New ID
--------- -------- ------
1 1123 1
2 1123 2
3 1124 1
4 1125 1
5 1125 2
6 1125 3
7 1125 4
Is there any way of doing this without looping/cursoring? If looping/cursoring is the only way, what would the most efficient code be?
Thanks
One method is to use ROW_NUMBER() OVER(PARTITION BY ... ORDER BY ...) in an UPDATE...FROM statement with a subquery in the FROM clause.
update MyTable set NewID = B.NewID
from
MyTable as A
inner join (select UniqueID, ROW_NUMBER() over (partition by GroupID order by UniqueID) as NewID from MyTable) as B on B.UniqueID = A.UniqueID
MSDN has a good sample to get you started:
You need to utilize a subquery in the FROM clause in order to utilize a windows function (Row_Index())
Partition By tells the server when to reset the row numbers
Order By tells the server which way to order the group's NewID's
I agree with Damien's point in the comments but you don't need a JOIN you can just update the CTE directly.
;WITH cte AS
(
SELECT [New ID],
ROW_NUMBER() OVER (PARTITION BY [Group ID] ORDER BY [Unique ID]) AS _NewID
FROM #T
)
UPDATE cte
SET [New ID] = _NewID
Online Demo
Alternate to RowNumber() if you're on SS 2000
SELECT UniqueID,
GroupID,
(SELECT COUNT(T2.GroupID)
FROM myTable T2
WHERE GroupID <= T1.GroupID) AS NewID
FROM myTable T1
This solution will also work, if you are running an old version of mssql
--Test table:
DECLARE #t table(Unique_ID int, Group_ID int, New_ID int)
--Test data:
INSERT #t (unique_id, group_id)
SELECT 1, 1123 UNION ALL SELECT 2, 1123 UNION ALL SELECT 3, 1124 UNION ALL SELECT 4, 1125 UNION ALL SELECT 5, 1125 UNION ALL SELECT 6, 1125 UNION ALL SELECT 7, 1125
--Syntax:
UPDATE t
SET new_id =
(SELECT count(*)
FROM #t
WHERE t.unique_id >= unique_id and t.group_id = group_id
GROUP BY group_id)
FROM #t t
--Result:
SELECT * FROM #t
Unique_ID Group_ID New_ID
----------- ----------- -----------
1 1123 1
2 1123 2
3 1124 1
4 1125 1
5 1125 2
6 1125 3
7 1125 4
SELECT
UniqueId,
GroupID,
ROW_NUMBER() OVER (PARTITION BY GroupId ORDER BY UniqueId) AS NewIdx
FROM
....