Cursor within a cursor - database

I need your help.
I am trying to make a series of cursors within other cursors.
Below I show you the tables with which I want to make the cursors.
First make the cursor of the first table looking for record.
Second, in table 1 we have the column "ID_ASI", with that column I want to make another cursor that searches inside another table (IMAGE OF TABLE 2) all the "ID_ASI" that it finds with the same "ID_ASI".
Finally, the "ID_ASI" finding by the second step, make a new cursor that looks for all "ID_DOC" that have the same "ID_ASI".
For example,
In the second step, when making the cursor in the "ID_ASI" column, find 3 rows with the same "ID" (101), then the third step searches for all "ID_DOC" with the same "ID_ASI".
For example, "ID_ASI" 101 has 3 "ID_DOC" (value 10), 101 has 2 other values ​​(20) and finally two other values ​​(30).
The complicated thing is how to group them all in the same way, and how to put a cursor inside a cursor.
This would be the result.
Thank you for attention.

To me it looks like what you want is to join the tables together, something like
SELECT t2.*
FROM TABLE_1 t1
INNER JOIN TABLE_2 t2
ON t2.ID_ASI = t1.ID_ASI
ORDER BY t2.ID_ASI, t2.ID_DOC
SQLFiddle here
Best of luck.

Use a hierarchical query:
SQL Fiddle
Oracle 11g R2 Schema Setup:
CREATE TABLE TABLE_1 ( ID_ASI ) AS
SELECT 101 FROM DUAL UNION ALL
SELECT 201 FROM DUAL UNION ALL
SELECT 301 FROM DUAL;
CREATE TABLE TABLE_2 (ID_ASI, ID_DOC, IMPORT, IMPORT_TO ) AS
SELECT 101, 10, NULL, 1000 FROM DUAL UNION ALL
SELECT 101, 20, NULL, 2000 FROM DUAL UNION ALL
SELECT 101, 30, NULL, 3000 FROM DUAL UNION ALL
SELECT 201, 23, NULL, 430 FROM DUAL UNION ALL
SELECT 201, 23, 430, NULL FROM DUAL UNION ALL
SELECT 104, 10, 500, NULL FROM DUAL UNION ALL
SELECT 104, 20, 2000, NULL FROM DUAL UNION ALL
SELECT 104, 10, 500, NULL FROM DUAL UNION ALL
SELECT 104, 30, 3000, NULL FROM DUAL;
Query 1:
SELECT *
FROM TABLE_2
START WITH ID_ASI IN ( SELECT ID_ASI FROM TABLE_1 )
CONNECT BY PRIOR ID_DOC = ID_DOC
AND PRIOR ID_ASI < ID_ASI
ORDER SIBLINGS BY 1, 2, 3, 4
Results:
| ID_ASI | ID_DOC | IMPORT | IMPORT_TO |
|--------|--------|--------|-----------|
| 101 | 10 | (null) | 1000 |
| 104 | 10 | 500 | (null) |
| 104 | 10 | 500 | (null) |
| 101 | 20 | (null) | 2000 |
| 104 | 20 | 2000 | (null) |
| 101 | 30 | (null) | 3000 |
| 104 | 30 | 3000 | (null) |
| 201 | 23 | 430 | (null) |
| 201 | 23 | (null) | 430 |

Related

Retrieve connected rows in SQL Server

I have this table in SQL Server:
+--------------+---------------------+
| AccountId | AccountIdAssociated |
+--------------+---------------------+
| 2 | 3 |
| 3 | 15 |
| 1 | 30 |
| 3 | 12 |
| 12 | 10 |
| 10 | 50 |
| 19 | 32 |
| 18 | 33 |
+--------------+---------------------+
As you can see accounts 2, 3, 10, 12, 15, and 50 are connected to each other either directly or transitively how can I retrieve all these connected AccountIds by using only one number (let's say AccountId = 2)
What you need here are a couple of rCTEs to "traverse" the hierarchical data:
WITH VTE AS(
SELECT *
FROM (VALUES(2 ,3 ),
(3 ,15),
(1 ,30),
(3 ,12),
(12,10),
(19,32),
(18,33))V(AccountID,AccountIdAssociated)),
rCTEUp AS(
SELECT V.AccountID,
V.AccountIdAssociated
FROM VTE V
WHERE V.AccountID = 3
UNION ALL
SELECT V.AccountID,
V.AccountIdAssociated
FROM VTE V
JOIN rCTEUp r ON V.AccountIdAssociated = r.AccountID),
rCTEDown AS(
SELECT V.AccountID,
V.AccountIdAssociated
FROM VTE V
WHERE V.AccountID = 3
UNION ALL
SELECT V.AccountID,
V.AccountIdAssociated
FROM VTE V
JOIN rCTEDown r ON V.AccountID = r.AccountIdAssociated)
SELECT AccountID,
AccountIdAssociated
FROM rCTEUp
UNION ALL
SELECT AccountID,
AccountIdAssociated
FROM rCTEDown
WHERE AccountID != 3;

TSQL calculate row value based on value in previous row in same column

I have a dataset where I need to calculate a value that for each row depends on the value in the previous row of the same column. Or a 1 initially when there is no previous row. I need to do this on different partitions.
The formula looks like this: factor = (previous factor or 1 if it does not exist) * (1 + div / nav)
This needs to be partitioned by Inst_id.
I would prefer to avoid a cursor. Maybe cte with recursion - but I cannot get my head around it - or another way?
I know this code does not work as I cannot reference the same column, but it is another way of showing what I'm trying to do:
SELECT Dato, Inst_id, nav, div
, (1 + div / nav ) * ISNULL(LAG(factor, 1) OVER (PARTITION BY Inst_id ORDER BY Date), 1) AS factor
FROM #tmp
So with my test data I need to get these results in the factor column below.
Please ignore rounding issues, as I calculated this in Excel:
date Inst_id nav div factor
11-04-2012 16 57.5700 5.7500 1.09987841
19-04-2013 16 102.8600 10.2500 1.20948130
29-04-2014 16 65.9300 16.7500 1.51675890
08-04-2013 29 111.2736 17.2500 1.15502333
10-04-2014 29 101.9650 16.3000 1.33966395
15-04-2015 29 109.5400 7.5000 1.43138825
27-04-2016 29 94.2500 0.4000 1.43746311
15-04-2015 34 159.1300 11.4000 1.07163954
27-04-2016 34 124.6100 17.6000 1.22299863
26-04-2017 34 139.7900 9.2000 1.30348784
01-04-2016 38 99.4600 0.1000 1.00100543
26-04-2017 38 102.9200 2.1000 1.02143014
Test data:
DECLARE #tmp TABLE(Dato DATE, Inst_id INT, nav DECIMAL(26,19), div DECIMAL(26,19), factor DECIMAL(26,19))
INSERT INTO #tmp (Dato, Inst_id, nav, div) VALUES
('2012-04-11', 16, 57.57, 5.75),
('2013-04-19', 16, 102.86, 10.25),
('2014-04-29', 16, 65.93, 16.75),
('2013-04-08', 29, 111.273577, 17.25),
('2014-04-10', 29, 101.964994, 16.3),
('2015-04-15', 29, 109.54, 7.5),
('2016-04-27', 29, 94.25, 0.4),
('2015-04-15', 34, 159.13, 11.4),
('2016-04-27', 34, 124.61, 17.6),
('2017-04-26', 34, 139.79, 9.2)
I'm on a Microsoft SQL Server Enterprise 2016 (and use SSMS 2016).
You can use (if DIV and NAV are always >0):
SELECT A.* , EXP(SUM( LOG(1+DIV/NAV) ) OVER (PARTITION BY INST_ID ORDER BY DATO) )AS FACT_NEW
FROM #tmp A
Actually what you need is an equivalent of aggregate function MULTIPLY() OVER ....
Using a log theorem: LOG(M*N) = LOG(M) + LOG (N) you can do it; for example:
DECLARE #X1 NUMERIC(10,4)=5
DECLARE #X2 NUMERIC(10,4)=7
SELECT #x1*#x2 AS S1, EXP(LOG(#X1)+LOG(#X2)) AS S2
Output:
+------------+---------+-------------------------+------------------------+--------+------------------+
| Dato | Inst_id | nav | div | factor | FACT_NEW |
+------------+---------+-------------------------+------------------------+--------+------------------+
| 2012-04-11 | 16 | 57.5700000000000000000 | 5.7500000000000000000 | NULL | 1.099878408893 |
| 2013-04-19 | 16 | 102.8600000000000000000 | 10.2500000000000000000 | NULL | 1.20948130303111 |
| 2014-04-29 | 16 | 65.9300000000000000000 | 16.7500000000000000000 | NULL | 1.51675889783963 |
| 2013-04-08 | 29 | 111.2735770000000000000 | 17.2500000000000000000 | NULL | 1.155023325977 |
| 2014-04-10 | 29 | 101.9649940000000000000 | 16.3000000000000000000 | NULL | 1.33966395090911 |
| 2015-04-15 | 29 | 109.5400000000000000000 | 7.5000000000000000000 | NULL | 1.43138824917236 |
| 2016-04-27 | 29 | 94.2500000000000000000 | 0.4000000000000000000 | NULL | 1.43746310646293 |
| 2015-04-15 | 34 | 159.1300000000000000000 | 11.4000000000000000000 | NULL | 1.071639539998 |
| 2016-04-27 | 34 | 124.6100000000000000000 | 17.6000000000000000000 | NULL | 1.22299862758278 |
| 2017-04-26 | 34 | 139.7900000000000000000 | 9.2000000000000000000 | NULL | 1.30348784264639 |
+------------+---------+-------------------------+------------------------+--------+------------------+
Using recursive CTE:
WITH DataSource AS
(
SELECT *
,ROW_NUMBER() OVER (PARTITION BY Inst_id ORDER BY Dato) AS [rowId]
FROM #tmp
),
RecursiveDataSource AS
(
SELECT *
,CAST((1 + div / nav ) * 1 AS DECIMAL(26,19)) as [factor_calculated]
FROM DataSource
WHERE [rowId] = 1
UNION ALL
SELECT A.*
,CAST((1 + A.div / A.nav ) * R.factor_calculated AS DECIMAL(26,19)) as [factor_calculated]
FROM RecursiveDataSource R
INNER JOIN DataSource A
ON r.[Inst_id] = A.[Inst_id]
AND R.[rowId] + 1 = A.[rowId]
)
SELECT *
FROM RecursiveDataSource
ORDER BY Inst_id, Dato;
I guess you are getting different values in Excel after row 3, because you are not partitioning by Inst_id there.

select query to get first row from rows having multiple id's.(without partition by)

id date amount documentNo paperID
1 2015/10/15 500 1234 34
1 2015/10/15 100 1332 33
2 2015/10/13 200 1302 21
2 2015/10/13 400 1332 33
3 2015/11/23 500 1332 43
I should get the output as:
id date amount documentNo paperID
1 2015/10/15 500 1234 34
2 2015/10/13 200 1302 21
3 2015/11/23 500 1332 43
Please suggest a simple select query to fetch only one row without partition by. Note: the date remain same for a particular id.
Try a null-self-join. Basically you are comparing each row to some other version of that row ,but, via an inequality (here I have used documentNo) you end-up with a single row that has no match.
See this SQL Fiddle
MySQL 5.6 Schema Setup:
CREATE TABLE Table1
(`id` int, `date` datetime, `amount` int, `documentNo` int, `paperID` int)
;
INSERT INTO Table1
(`id`, `date`, `amount`, `documentNo`, `paperID`)
VALUES
(1, '2015-10-15 00:00:00', 500, 1234, 34),
(1, '2015-10-15 00:00:00', 100, 1332, 33),
(2, '2015-10-13 00:00:00', 200, 1302, 21),
(2, '2015-10-13 00:00:00', 400, 1332, 33),
(3, '2015-11-23 00:00:00', 500, 1332, 43)
;
Query 1:
SELECT
t1.*
FROM table1 AS t1
LEFT OUTER JOIN table1 AS t2 ON t1.id = t2.id
AND t1.date = t2.date
AND t2.documentNo < t1.documentNo
WHERE t2.ID IS NULL
Results:
| id | date | amount | documentNo | paperID |
|----|----------------------------|--------|------------|---------|
| 1 | October, 15 2015 00:00:00 | 500 | 1234 | 34 |
| 2 | October, 13 2015 00:00:00 | 200 | 1302 | 21 |
| 3 | November, 23 2015 00:00:00 | 500 | 1332 | 43 |
EDIT: There are several approaches to this problem even without windowing functions such as row_number() , here is a previous answer covering some MySQL specific alternatives.

Running "Group By" Ordinal Counter Based on a "Flip" Column

Usually I'm decent at set-based tsql problems. But this one is beating me.
I've been working 3 days on converting a while-loop procedure into a setbased one. I've gotten to the point below.......but can't make the final jump.
I have the following rows. MyOrdinal will be "in order" ... and a second column (MyMarker) will alternate between having a value and being null. Whenever this "flip" occurs on MyMarker, I would like to increment a "group by" ordinal counter by one. Whenever the "flip" values are non-null or null, these are grouped together as a set.
I've tried several things, but it was too ugly to post. That and since moving to ORM, I don't spend as much time in the tsql anymore.
declare #Holder table ( MyOrdinal int not null , MyMarker int , MyGroupNumber int )
INSERT INTO #Holder (MyOrdinal, MyMarker)
Select 1 , 1
union all Select 2, 2
union all Select 3, null
union all Select 4, 3
union all Select 5, 4
union all Select 6, 5
union all Select 7, 6
union all Select 8, 7
union all Select 9, 8
union all Select 10, 9
union all Select 11, 10
union all Select 12, 11
union all Select 13, 12
union all Select 14, 13
union all Select 15, 14
union all Select 16, 15
union all Select 17, null
union all Select 18, null
union all Select 19, null
union all Select 20, 16
union all Select 21, 17
union all Select 22, 18
union all Select 23, null
union all Select 24, null
union all Select 25, 19
union all Select 26, 20
union all Select 27, null
union all Select 28, 21
Select * from #Holder
Desired Output
| MyOrdinal | MyMarker | MyGroupNumber |
|-----------|----------|---------------|
| 1 | 1 | 1 |
| 2 | 2 | 1 |
| 3 | null | 2 |
| 4 | 3 | 3 |
| 5 | 4 | 3 |
| 6 | 5 | 3 |
| 7 | 6 | 3 |
| 8 | 7 | 3 |
| 9 | 8 | 3 |
| 10 | 9 | 3 |
| 11 | 10 | 3 |
| 12 | 11 | 3 |
| 13 | 12 | 3 |
| 14 | 13 | 3 |
| 15 | 14 | 3 |
| 16 | 15 | 3 |
| 17 | null | 4 |
| 18 | null | 4 |
| 19 | null | 4 |
| 20 | 16 | 5 |
| 21 | 17 | 5 |
| 22 | 18 | 5 |
| 23 | null | 6 |
| 24 | null | 6 |
| 25 | 19 | 7 |
| 26 | 20 | 7 |
| 27 | null | 8 |
| 28 | 21 | 9 |
Try this one:
First, this assigns a same ROW_NUMBER for continuous Non-NULL MyMarker. ROW_NUMBER is NULL for NULL MyMarkers. After that, you want to add a ROW_NUMBER for NULL MyMarkers such that the value is between the previous NON-NULL and the next NON-NULL. Then use DENSE_RANK to finally assign MyGroupNumber:
SQL Fiddle
;WITH Cte AS(
SELECT *,
RN = ROW_NUMBER() OVER(ORDER BY MyOrdinal) - MyMarker + 1
FROM #Holder
),
CteApply AS(
SELECT
t.MyOrdinal,
t.MyMarker,
MyGroupNumber =
CASE
WHEN RN IS NULL THEN x.NewRN
ELSE RN
END
FROM Cte t
OUTER APPLY(
SELECT TOP 1 RN * 1.1 AS NewRN
FROM Cte
WHERE
t.MyOrdinal > MyOrdinal
AND MyMarker IS NOT NULL
ORDER BY MyOrdinal DESC
)x
)
SELECT
MyOrdinal,
MyMarker,
MyGroupNumber = DENSE_RANK() OVER(ORDER BY MyGroupNumber)
FROM CteApply
For Sql Server 2012:
select *, sum(b) over(order by myordinal)
from(select *,
case when (lag(mymarker) over(order by myordinal) is not null
and mymarker is null) or
(lag(mymarker) over(order by myordinal) is null
and mymarker is not null)
then 1 else 0 end as b
from #Holder) t
First you mark rows with 1 where there is a change from null to not null or from not null to null. Other columns are marked as 0. Then running sum of all rows till current.
Fiddle http://sqlfiddle.com/#!6/9eecb/5015
For Sql Server 2008:
with cte1 as (select *,
case when (select max(enddate) from t ti
where ti.ruleid = t.ruleid and ti.startdate < t.startdate) = startdate
then 0 else 1 end as b
from t),
cte2 as(select *, sum(b) over(partition by ruleid order by startdate) as s
from cte1)
select RuleID,
Name,
min(startdate),
case when count(*) = count(enddate)
then max(enddate) else null end from cte2
group by s, ruleid, name
Fiddle http://sqlfiddle.com/#!6/4191d/6

How Merge Duplicate Data using MergeTable?

There are duplicate record in my table. I wrote a query to find them. The result is like this:
+-----------+-------------+-------------+
| Row | NationalNo | Client ID |
+-----------+-------------+-------------+
| 1 | 10003 | 34 |
+-----------+-------------+-------------+
| 2 | 10003 | 75 |
+-----------+-------------+-------------+
| 1 | 20023 | 23 |
+-----------+-------------+-------------+
| 2 | 20023 | 55 |
+-----------+-------------+-------------+
| 3 | 20023 | 12 |
+-----------+-------------+-------------+
The above result means we have one client with National-No of 10003 whom inserted twice and another client with National-No of 20023 whom inserted 3 time in Client table.
But I am not going to delete the extra. I want to keep the first record active and the rest will be inactive.
The Task is to save this actions as History IN MergeTable. MergeTable has 3 Columns: ClientIDA, ClientIDB, Date
I want to Consider the records with Row of 1 As ClientIDA and rest of them As ClientIDB.
So the output needed to insert into MergeTable is:
+-----------+-----------+-------------+
| ClientIDA | ClientIDB | Date |
+-----------+-----------+-------------+
| 34 | 75 | 2014-06-10 |
+-----------+-----------+-------------+
| 23 | 55 | 2014-06-10 |
+-----------+-----------+-------------+
| 23 | 12 | 2014-06-10 |
+-----------+-----------+-------------+
Here is example how u can do.
You split your table into two (data which you insert and data which will not)
And then you just join this two tables.
DECLARE #duplicates TABLE (Row INT, NationalNo INT, ClientID INT)
INSERT INTO #duplicates (Row, NationalNo, ClientID) SELECT 1, 10003, 34
INSERT INTO #duplicates (Row, NationalNo, ClientID) SELECT 2, 10003, 75
INSERT INTO #duplicates (Row, NationalNo, ClientID) SELECT 1, 20023, 23
INSERT INTO #duplicates (Row, NationalNo, ClientID) SELECT 2, 20023, 55
INSERT INTO #duplicates (Row, NationalNo, ClientID) SELECT 3, 20023, 12
;WITH ClientIDA AS (
SELECT Row, NationalNo, ClientID
FROM #duplicates
WHERE Row = 1
), ClientIDB AS (
SELECT Row, NationalNo, ClientID
FROM #duplicates
WHERE Row != 1
)
SELECT A.ClientID AS ClientIDA, B.ClientID AS ClientIDB, GETDATE() AS DATE
FROM ClientIDB AS B
INNER JOIN ClientIDA AS A
ON A.NationalNo = B.NationalNo

Resources