I have a table with this data:
ID | Data
----+-----
1 | ABC
2 | A
3 | A
4 | AC
5 | B
I would like to extract for each row each character and the ID from it come like this:
ID | Data_extract
----+-----------
1 | A
1 | B
1 | C
2 | A
3 | A
4 | A
4 | C
5 | B
I'm using Microsoft SQL Server
Thanks
You can use Common table expressions to achieve this.
DECLARE #t table(ID int, val VARCHAR(10))
INSERT INTO #t
VALUES
(1 ,'ABC')
,(2 ,'A')
,(3 ,'A')
,(4 ,'AC')
,(5 ,'B');
;WITH CTE_VAL AS
(
SELECT id,
cast((case when len(val) > 1 then left(val,1) else val end) as nvarchar(50)) as nval, 1 as lvl
from #t
UNION ALL
SELECT t.id,
cast(substring(val,lvl+1,1) as nvarchar(50)) as nval, lvl+1
FROM #t AS t INNER JOIN CTE_VAL AS c
ON t.ID = c.ID
WHERE len(val) > 1 and lvl < len(val)
)
SELECT id,nval FROM CTE_VAL
order by id
+----+------+
| id | nval |
+----+------+
| 1 | A |
| 1 | B |
| 1 | C |
| 2 | A |
| 3 | A |
| 4 | A |
| 4 | C |
| 5 | B |
+----+------+
Related
I have a situation where I am required to create a copy of the data of one table within itself with a different range of foreign key in one of the columns. For example:
--------------------------------------------------------------
|TYPES |ITEMS |SUBITEMS |
|--------------|----------------------|----------------------|
| ID | VALUE | ID | VALUE | TYPEID | ID | VALUE | ITEMID |
|----|---------|----|--------|--------|----|--------|--------|
| 1 | TYPE1 | 1 | ITEMA | 1 | 1 | SUB1 | 1 |
| 2 | TYPE2 | 2 | ITEMB | 1 | 2 | SUB2 | 2 |
| | | 3 | ITEMC | 1 | 3 | SUB3 | 3 |
| | | 4 | ITEMD | 2 | | | |
| | | 5 | ITEME | 2 | | | |
| | | 6 | ITEMF | 2 | | | |
--------------------------------------------------------------
Here I have to copy from SUBITEMS and insert back but with ITEMIDs that have TYPEID as 2 resulting in the following example:
--------------------------------------------------------------
|TYPES |ITEMS |SUBITEMS |
|--------------|----------------------|----------------------|
| ID | VALUE | ID | VALUE | TYPEID | ID | VALUE | ITEMID |
|----|---------|----|--------|--------|----|--------|--------|
| 1 | TYPE1 | 1 | ITEMA | 1 | 1 | SUB1 | 1 |
| 2 | TYPE2 | 2 | ITEMB | 1 | 2 | SUB2 | 2 |
| | | 3 | ITEMC | 1 | 3 | SUB3 | 3 |
| | | 4 | ITEMD | 2 | 4 | SUB1 | 4 |
| | | 5 | ITEME | 2 | 5 | SUB2 | 5 |
| | | 6 | ITEMF | 2 | 6 | SUB3 | 6 |
--------------------------------------------------------------
EDIT 2: If the amount of rows differ in either of the tables (4 Items while 3 SubItems or 3 Items while 4 SubItems) then only those rows should be considered that are enough for a 1:1 relation between the two tables (3 result since that is the least count among either) as shown in the following example.
--------------------------------------------------------------
|TYPES |ITEMS |SUBITEMS |
|--------------|----------------------|----------------------|
| ID | VALUE | ID | VALUE | TYPEID | ID | VALUE | ITEMID |
|----|---------|----|--------|--------|----|--------|--------|
| 1 | TYPE1 | 1 | ITEMA | 1 | 1 | SUB1 | 1 |
| 2 | TYPE2 | 2 | ITEMB | 1 | 2 | SUB2 | 2 |
| | | 3 | ITEMC | 1 | 3 | SUB3 | 3 |
| | | 4 | ITEMD | 2 | 4 | SUB1 | 4 |
| | | 5 | ITEME | 2 | 5 | SUB2 | 5 |
| | | 6 | ITEMF | 2 | 6 | SUB3 | 6 |
| | | 7 | ITEMG | 2 | | | |
--------------------------------------------------------------
Of course the actual data isn't as simple and has many other types and items n subitems and the required IDs would be missing some sequence like 10001, 10008, 40042, etc with many other columns all defining what data is being copied and which IDs need to be thrown over them. It's just the matter of how each data row obtained should get mapped 1:1 to each ID obtained (assuming both as if in their own temp tables before the moment of this merger). Following is a sample of what I am able to do so far:
CREATE TABLE #SubItemsTemp (Value VARCHAR(100))
CREATE TABLE #ItemIDsTemp (TypeID INT)
INSERT INTO #SubItemsTemp (Value)
SELECT
SI.Value
FROM
SubItems SI
JOIN Items IT ON SI.ItemID = IT.ID
WHERE
IT.TypeID = 1
INSERT INTO #ItemIDsTemp(Value)
SELECT IT.ID
FROM Items IT
WHERE IT.TypeID = 2
--What next?
EDIT 1: Forgot to mention the actual question line... How to insert them together into the SUBITEMS table such that the second example comes to fruition?
Footnote: This is a extreme simplification of the actual queries that have several joins to get to "TYPE"
Try this query. Query assumes that ID column in SUBITEMS table is identity and will work only with TypeId's 1 and 2
declare #TYPES table(ID int, VALUE varchar(100))
declare #ITEMS table(ID int, VALUE varchar(100), TYPEID int)
declare #SUBITEMS table(ID int identity(1,1), VALUE varchar(100), ITEMID int)
insert into #TYPES values (1, 'TYPE1'), (2, 'TYPE2')
insert into #ITEMS values (1, 'ITEMA', 1), (2, 'ITEMB', 1), (3, 'ITEMC', 1), (4, 'ITEMD', 2), (5, 'ITEME', 2), (6, 'ITEMF', 2), (7, 'ITEMG', 2)
insert into #SUBITEMS values ('SUB1', 1), ('SUB2', 2), ('SUB3', 3)
; with cte_1 as (
select
s.VALUE, rn = row_number() over (order by i.ID)
from
#ITEMS i
join #SUBITEMS s on s.ITEMID = i.ID
where
i.TYPEID = 1
)
, cte_2 as (
select
ID, rn = row_number() over (order by ID)
from
#ITEMS
where
TYPEID = 2
)
insert into #SUBITEMS
select
a.VALUE, b.ID
from
cte_1 a
join cte_2 b on a.rn = b.rn
select * from #SUBITEMS
Output
ID Value ItemId
------------------
1 SUB1 1
2 SUB2 2
3 SUB3 3
4 SUB1 4
5 SUB2 5
6 SUB3 6
I need help with ranking of rows in one table.
+-----+-------+-------------+------------+-------+------+
| ID | group | typeInGroup | rankOfType | score | Rank |
+-----+-------+-------------+------------+-------+------+
| 1 | a | type1 | 1 | 40 | |
| 2 | a | type2 | 2 | 55 | |
| 3 | a | type1 | 1 | 20 | |
| 4 | b | type3 | 3 | 80 | |
| 5 | b | type2 | 2 | 60 | |
| 6 | b | type1 | 1 | 70 | |
| 7 | b | type1 | 1 | 70 | |
+-----+-------+-------------+------------+-------+------+
I am basically looking for solution which would give me order for last column "Rank".
Each "group" has up to 9 "typeInGroup" which are ranked by 1-9 in column "rankOfTypes". Each "typeInGroup" has "score". When i am calculating last column "Rank" i look at the "score" and "rankOfType" column.
The row with the higgest score should be ranked first unless there is row with "rankOfType" column that has lower value and score that is <= 15 than the score we have been looking at. Order of rows with same "score" and "rankOfType" is not important.
I should do this check for every single row in group and in the end end with something like this:
+-----+-------+-------------+------------+-------+------+
| ID | group | typeInGroup | rankOfType | score | Rank |
+-----+-------+-------------+------------+-------+------+
| 1 | a | type1 | 1 | 40 | 1 |
| 2 | a | type2 | 2 | 55 | 2 |
| 3 | a | type1 | 1 | 20 | 3 |
| 4 | b | type3 | 3 | 80 | 3 |
| 5 | b | type2 | 2 | 60 | 4 |
| 6 | b | type1 | 1 | 70 | 1 |
| 7 | b | type1 | 1 | 70 | 2 |
+-----+-------+-------------+------------+-------+------+
Any idea how to do this?
the CROSS APPLY query, checks for any rows that meet your special requirement, if exists, than that row will have higher priority
try it out with larger data set and verify the result
declare #tbl table
(
ID int,
Grp char,
typeInGrp varchar(5),
rankOfType int,
score int
)
insert into #tbl select 1, 'a', 'type1', 1, 40
insert into #tbl select 2, 'a', 'type2', 2, 55
insert into #tbl select 3, 'a', 'type1', 1, 20
insert into #tbl select 4, 'b', 'type3', 3, 80
insert into #tbl select 5, 'b', 'type2', 2, 60
insert into #tbl select 6, 'b', 'type1', 1, 70
insert into #tbl select 7, 'b', 'type1', 1, 70
select *,
[rank] = row_number() over (partition by Grp
order by case when cnt > 0 then 1 else 2 end,
score desc)
from #tbl t
cross apply
(
select cnt = count(*)
from #tbl x
where x.Grp = t.Grp
and x.ID <> t.ID
and x.rankOfType > t.rankOfType
and x.score - t.score <= 15
) s
order by ID
+----+--------+-----------+
| ID | ID_DEP | OPERATION |
+----+--------+-----------+
| 1 | 2 | T1 |
| 2 | 2 | T1 |
| 3 | 2 | T4 |
| 4 | 1 | T1 |
| 5 | 1 | T1 |
| 6 | 1 | T4 |
| 7 | 4 | T6 |
| 8 | 3 | T1 |
| 9 | 3 | T1 |
| 10 | 5 | T9 |
+----+--------+-----------+
Hey guys, help me with that simple question.
How to select only these id's (ID_DEP) that have ONLY T1 from Operation column and not other value.
I'd use a WHERE NOT EXISTS
Test Data
IF OBJECT_ID('tempdb..#TestData') IS NOT NULL DROP TABLE #TestData
CREATE TABLE #TestData (ID int, ID_DEP int, OPERATION varchar(2))
INSERT INTO #TestData (ID, ID_DEP, OPERATION)
VALUES
(1,2,'T1')
,(2,2,'T1')
,(3,2,'T4')
,(4,1,'T1')
,(5,1,'T1')
,(6,1,'T4')
,(7,4,'T6')
,(8,3,'T1')
,(9,3,'T1')
,(10,5,'T9')
Query
SELECT
td.*
FROM #TestData td
WHERE NOT EXISTS (SELECT 1 FROM #TestData sub WHERE sub.OPERATION <> 'T1' AND td.ID_DEP = sub.ID_DEP)
Output
ID ID_DEP OPERATION
8 3 T1
9 3 T1
Basically, the NOT EXISTS returns any rows where the value IS NOT 'T1'. If an ID_DEP exists then we want to exclude this from the result set.
I have this table
+-----+------+--------+--------+
| ID | Name | Start | End |
+-----+------+--------+--------+
| 20 | Mike | 1 | 3 |
| 21 | Luke | 4 | 7 |
+-----+------+--------+--------+
And I want to generate all rows based on the range (start / end) of each person.
The outcome should be this
+-----+------+-----------------+
| ID | Name | Start_End |
+-----+------+-----------------+
| 20 | Mike | 1 |
| 20 | Mike | 2 |
| 20 | Mike | 3 |
| 21 | Luke | 4 |
| 21 | Luke | 5 |
| 21 | Luke | 6 |
| 21 | Luke | 7 |
+-----+------+--------+--------+
To get unique values based on Start and End column, I have this function
CREATE FUNCTION [dbo].[ufn_SplitRange] (#Start INT, #End INT)
RETURNS TABLE
AS
RETURN
(
SELECT TOP (#End - #Start+1) ROW_NUMBER() OVER (ORDER BY S.[object_id])+(#Start - 1) [Start_End]
FROM sys.all_objects S WITH (NOLOCK)
);
The above function returns the output of (based on Mike range of 1-3):
1
2
3
I have been trying several approaches and, I can't find the right solution, it seems a very common task, but a tricky one.
Any input is highly appreciated
using cross apply():
select t.Id, t.Name, x.Start_End
from t
cross apply dbo.ufn_SplitRange(t.Start,t.[End]) as x
rextester demo: http://rextester.com/FVA48693
returns:
+----+------+-----------+
| Id | Name | Start_End |
+----+------+-----------+
| 20 | Mike | 1 |
| 20 | Mike | 2 |
| 20 | Mike | 3 |
| 21 | Luke | 4 |
| 21 | Luke | 5 |
| 21 | Luke | 6 |
| 21 | Luke | 7 |
+----+------+-----------+
You can use tally table as below:
Select Id, Name, Start_end from #Values
cross apply (
Select top ([end] - [start] +1) Start_end = [start] + Row_number() over (order by (Select NULL))-1
from master..spt_values s1, master..spt_values s2
) a
Output :
+----+------+----+
| Id | Name | RN |
+----+------+----+
| 20 | Mike | 1 |
| 20 | Mike | 2 |
| 20 | Mike | 3 |
| 21 | Luke | 4 |
| 21 | Luke | 5 |
| 21 | Luke | 6 |
| 21 | Luke | 7 |
+----+------+----+
You could use recursive cte like this
DECLARE #SampleData AS TABLE
(
Id int,
Name varchar(10),
Start int,
[End] int
)
INSERT INTO #SampleData
(
Id,
Name,
Start,
[End]
)
VALUES
(1,'Mike',1,3),
(2,'Luke',4,7)
;WITH temp AS
(
SELECT Id, sd.Name, sd.Start , sd.[End]
FROM #SampleData sd
UNION ALL
SELECT t.Id, t.Name, t.Start + 1, t.[End]
FROM temp t
WHERE t.Start < t.[End]
)
SELECT t.Id, t.Name, t.Start AS [Start_End]
FROM temp t
ORDER BY t.Id
OPTION (MAXRECURSION 0)
Demo link: http://rextester.com/AFNYFW81782
I have the following table:
mysql> SELECT * FROM temp;
+----+------+
| id | a |
+----+------+
| 1 | 1 |
| 2 | 2 |
| 3 | 3 |
| 4 | 4 |
+----+------+
I am trying to get the following output:
+----+------+------+
| id | a | a |
+----+------+------+
| 1 | 1 | 2 |
| 2 | 2 | 3 |
| 3 | 3 | 4 |
+----+------+------+
but I am having a small problem. I wrote the following query:
mysql> SELECT A.id, A.a, B.a FROM temp A, temp B WHERE B.a>A.a;
but my output is the following:
+----+------+------+
| id | a | a |
+----+------+------+
| 1 | 1 | 2 |
| 1 | 1 | 3 |
| 2 | 2 | 3 |
| 1 | 1 | 4 |
| 2 | 2 | 4 |
| 3 | 3 | 4 |
+----+------+------+
Can someone tell me how to convert this into the desired output? I am trying to get a form where only the consecutive values are produced. I mean, if 2 is greater than 1 and 3 is greater than 2, I do not want 3 is greater than 1.
Option 1: "Triangular Join" - Quadratic Complexity
SELECT A.id, A.a, MIN(B.a) AS a
FROM temp A
JOIN temp B ON B.a>A.a
GROUP BY A.id, A.a;`
Option 2: "Pseudo Row_Number()" - Linear Complexity
select a_numbered.id, a_numbered.a, b_numbered.a
from
(
select id,
a,
#rownum := #rownum + 1 as rn
from temp
join (select #rownum := 0) r
order by id
) a_numbered join (
select id,
a,
#rownum2 := #rownum2 + 1 as rn
from temp
join (select #rownum2 := 0) r
order by id
) b_numbered
on b_numbered.rn = a_numbered.rn+1