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
Related
I have two tables.
Student Table
Property Table
Result Table
How can I get the value of Student Table and the property ID of the column fron the Property table and merge that into the Result table?
Any advice would be helpful.
Update #1:
I tried using Christian Moen 's suggestion, this is what i get.
You need to UNPIVOT the Student's columns first, to get the columns (properties names) in one column as rows. Then join with the Property table based on the property name like this:
WITH UnPivoted
AS
(
SELECT ID, value,col
FROM
(
SELECT ID,
CAST(Name AS NVARCHAR(50)) AS Name,
CAST(Class AS NVARCHAR(50)) AS Class,
CAST(ENG AS NVARCHAR(50)) AS ENG,
CAST(TAM AS NVARCHAR(50)) AS TAM,
CAST(HIN AS NVARCHAR(50)) AS HIN,
CAST(MAT AS NVARCHAR(50)) AS MAT,
CAST(PHY AS NVARCHAR(50)) AS PHY
FROM Student
) AS s
UNPIVOT
(value FOR col IN
([Name], [class], [ENG], [TAM], [HIN], [MAT], [PHY])
)AS unpvt
)
SELECT
ROW_NUMBER() OVER(ORDER BY u.ID,PropertyID) AS ID,
p.PropertyID,
u.Value,
u.ID AS StudID
FROM Property AS p
INNER JOIN UnPivoted AS u ON p.PropertyName = u.col;
For the first ID, I used the ranking function ROW_NUMBER() to generate this sequence id.
This will give the exact results that you are looking for.
Results:
| ID | PropertyID | Value | StudID |
|----|------------|--------|--------|
| 1 | 1 | Jack | 1 |
| 2 | 2 | 10 | 1 |
| 3 | 3 | 89 | 1 |
| 4 | 4 | 88 | 1 |
| 5 | 5 | 45 | 1 |
| 6 | 6 | 100 | 1 |
| 7 | 7 | 98 | 1 |
| 8 | 1 | Jill | 2 |
| 9 | 2 | 10 | 2 |
| 10 | 3 | 89 | 2 |
| 11 | 4 | 99 | 2 |
| 12 | 5 | 100 | 2 |
| 13 | 6 | 78 | 2 |
| 14 | 7 | 91 | 2 |
| 15 | 1 | Trevor | 3 |
| 16 | 2 | 12 | 3 |
| 17 | 3 | 100 | 3 |
| 18 | 4 | 50 | 3 |
| 19 | 5 | 49 | 3 |
| 20 | 6 | 94 | 3 |
| 21 | 7 | 100 | 3 |
| 22 | 1 | Jim | 4 |
| 23 | 2 | 8 | 4 |
| 24 | 3 | 100 | 4 |
| 25 | 4 | 91 | 4 |
| 26 | 5 | 92 | 4 |
| 27 | 6 | 100 | 4 |
| 28 | 7 | 100 | 4 |
Other option is to use of apply if you don't want to go unpivot way
select row_number() over (order by (select 1)) ID, p.PropertyID [PropID], a.Value, a.StuID
from Student s
cross apply
(
values (s.ID, 'Name', s.Name),
(s.ID, 'Class', cast(s.Class as varchar)),
(s.ID, 'ENG', cast(s.ENG as varchar)),
(s.ID, 'TAM', cast(s.TAM as varchar)),
(s.ID, 'HIN', cast(s.HIN as varchar)),
(s.ID, 'MAT', cast(s.MAT as varchar)),
(s.ID, 'PHY', cast(s.PHY as varchar))
) as a(StuID, Property, Value)
join Property p on p.PropertyName = a.Property
I have a database with a column (obj_id) in a table (parts) where I SHOULD have an obj_id of 12345 that is a set for another row that would have 12345|.
So:
select obj_id from parts where obj_id like '12345%';
12345
12345|A
12345|B
12345|77
Now, someone violated the guideline and put in some items with the piped-value but not the base value w/o the pipe (e.g. 12378|J, 12378|8 but not 12378).
I need to know how to write a SQL query to find these piped-values that do NOT have their matching base (non-piped) value in the table.
Without some realistic sample data to work with it's hard to know what you really want. Below a 2 queries that may be of assistance, but perhaps it will also make you note how useful sample data can be:
See this working at SQL Fiddle
CREATE TABLE PARTS
(id int, OBJ_ID varchar2(200))
;
INSERT ALL
INTO PARTS (id, OBJ_ID)
VALUES (1,'12345 12345|A 12345|B 12345|77')
INTO PARTS (id, OBJ_ID)
VALUES (2,'12346|A 12346|B 12346|77')
INTO PARTS (id, OBJ_ID)
VALUES (3,'12378|J, 12378|8')
INTO PARTS (id, OBJ_ID)
VALUES (4,NULL)
INTO PARTS (id, OBJ_ID)
VALUES (5,'fred. wilma, barney, betty')
SELECT * FROM dual
;
Query 1:
select
*
from PARTS p
where instr(p.OBJ_ID,' ') > instr(p.OBJ_ID,'|')
Results:
| ID | OBJ_ID |
|----|----------------------------|
| 2 | 12346|A 12346|B 12346|77 |
| 3 | 12378|J, 12378|8 |
| 5 | fred. wilma, barney, betty |
Query 2:
select
id, rn_a, regexp_substr (OBJ_ID_SPLIT, '[^|]+', 1, rn_b) as OBJ_ID_SPLIT
from (
select
p.id, c1.rn_a, regexp_substr (p.OBJ_ID, '[^ ]+', 1, c1.rn_a) as OBJ_ID_SPLIT
from PARTS p
cross join (select rownum as rn_a
from (select max(length (regexp_replace (OBJ_ID, '[^|]+'))) + 1 as mx
from PARTS
)
connect by level <= mx) c1
where p.OBJ_ID like '%|%'
) d
cross join (select 1 rn_b from dual union all select 2 from dual) c2
order by id, rn_a
Results:
| ID | RN_A | OBJ_ID_SPLIT |
|----|------|--------------|
| 1 | 1 | 12345 |
| 1 | 1 | (null) |
| 1 | 2 | 12345 |
| 1 | 2 | A |
| 1 | 3 | 12345 |
| 1 | 3 | B |
| 1 | 4 | 12345 |
| 1 | 4 | 77 |
| 2 | 1 | 12346 |
| 2 | 1 | A |
| 2 | 2 | 12346 |
| 2 | 2 | B |
| 2 | 3 | 12346 |
| 2 | 3 | 77 |
| 2 | 4 | (null) |
| 3 | 1 | 12378 |
| 3 | 1 | J, |
| 3 | 2 | 12378 |
| 3 | 2 | 8 |
| 3 | 3 | (null) |
| 3 | 4 | (null) |
let us assume I have Table A
| PK | Name |
-------------
| 1 | AA |
| 2 | BB |
| 3 | CC |
and table B
| PK | FK | Value |
-------------------
| 1 | 1 | i |
| 2 | 1 | j |
| 3 | 2 | x |
| 4 | 2 | y |
| 5 | 3 | l |
| 6 | 3 | k |
how can I select the below result
| PK | Name |
-------------
| 1 | AA |
| 1 | i |
| 2 | j |
| 2 | BB |
| 3 | x |
| 4 | y |
| 3 | CC |
| 3 | l |
| 4 | k |
List parents and under each parent list its children
Many Thanks for help
Very interesting table design. I believe it's just a matter of unioning your data and then ordering your results how you want them. If it's only a single level child-parent relationship, this should work just fine.
If Object_Id('tempdb..#TableA') Is Not Null Drop Table #TableA;
If Object_Id('tempdb..#TableB') Is Not Null Drop Table #TableB;
Select * Into #TableA
From (Values (1,'AA'),(2,'BB'),(3,'CC')) As a(PK,[Name])
Select * Into #TableB
From (Values (1,1,'i'),(2,1,'j'),(3,2,'x'),(4,2,'y'),(5,3,'l'),(6,3,'k')) As a(PK,FK,[Value])
;With Cte
As
(
Select PK,[Name],PK As OrderID,0 As LevelID /*Used to ensure parents are put above children*/
From #TableA
Union All
Select PK,[Value],FK,1
From #TableB
)
Select [PK], [Name]
From Cte
Order By OrderID,LevelID
Results:
PK | Name
1 | AA
1 | i
2 | j
2 | BB
3 | x
4 | y
3 | CC
5 | l
6 | k
Note: My last two rows(l and k) are a bit different than results. I assumed you it was typo when you put 3 and 4 as the id's rather than 5 and 6
I have a parent child hierarchy table. I am trying to return a list of all of the child ID's for each child ID. My table is defined as follows:
CREATE TABLE Organization_Hierarchy_Test (ORGANIZATION_ID INT, PARENT_ORG_ID INT);
INSERT INTO Organization_Hierarchy_Test (ORGANIZATION_ID, PARENT_ORG_ID)
VALUES(1,0), (2,1), (3,1), (4,2), (5,2), (6,2), (7,3), (8,3), (9,3), (10,3);
The results that I am after would look like this:
+-----------------+---------------+--------------------------+
| ORGANIZATION_ID | PARENT_ORG_ID | ORIGINAL_ORGANIZATION_ID |
+-----------------+---------------+--------------------------+
| 1 | 0 | 1 |
| 2 | 1 | 1 |
| 3 | 1 | 1 |
| 4 | 2 | 1 |
| 5 | 2 | 1 |
| 6 | 2 | 1 |
| 7 | 3 | 1 |
| 8 | 3 | 1 |
| 9 | 3 | 1 |
| 10 | 3 | 1 |
| 2 | 0 | 2 |
| 3 | 0 | 2 |
| 4 | 1 | 2 |
| 5 | 1 | 2 |
| 6 | 1 | 2 |
| 7 | 1 | 2 |
| 8 | 1 | 2 |
| 9 | 1 | 2 |
| 10 | 1 | 2 |
| 4 | 0 | 4 |
| 5 | 0 | 4 |
| 6 | 0 | 4 |
| 7 | 0 | 4 |
| 8 | 0 | 4 |
| 9 | 0 | 4 |
| 10 | 0 | 4 |
+-----------------+---------------+--------------------------+
The query that I have written gets me a list of all of the descendants for each organization_id, but I can not figure out how to return the same organization_id that is in fact related to all of the descendants.
I have tried adding a group by and returning the max id with little luck. I have a delivery date tomorrow and I am worried that I am not going to be able to work through this in time.
with descendants as
( select PARENT_ORG_ID, ORGANIZATION_ID, 1 as level
from Organization_Hierarchy_Test OH
union all
select d.PARENT_ORG_ID , OH1.ORGANIZATION_ID, d.level + 1
from descendants as d
join Organization_Hierarchy_Test OH1 on d.ORGANIZATION_ID = OH1.PARENT_ORG_ID
)
select ORGANIZATION_ID, PARENT_ORG_ID, level
from descendants
order by level, PARENT_ORG_ID, ORGANIZATION_ID
Any ideas on how to return the original Organization_ID along with all of the descendant organization_id's?
I am trying to push this to a tabular model and this will save me loads of time in processing the data.
Thanks very much in advance.
Change your CTE to simply include an extra column d.ORGANIZATION_ID AS Orig:
with descendants as
( select PARENT_ORG_ID, ORGANIZATION_ID, ORGANIZATION_ID AS Orig, 1 as level
from Organization_Hierarchy_Test OH
union all
select d.PARENT_ORG_ID , OH1.ORGANIZATION_ID, d.ORGANIZATION_ID AS Orig, d.level + 1
from descendants as d
join Organization_Hierarchy_Test OH1 on d.ORGANIZATION_ID = OH1.PARENT_ORG_ID
)
The current table is not setup for growth and I'd like to migrate the existing data to a table better suited for expansion. Let me explain:
The current table is set like:
+--------+---------------+----+----+----+------+-----------+
| id | DateOfService | AM | MD | PM | RATE | CLIENT_ID |
+--------+---------------+----+----+----+------+-----------+
| 1 | 3/4/2013 | 1 | 0 | 0 | 10 | 123 |
| 2 | 3/5/2013 | 1 | 0 | 0 | 10 | 123 |
| 3 | 3/6/2013 | 1 | 0 | 0 | 10 | 123 |
| 4 | 3/5/2013 | 0 | 1 | 1 | 50 | 147 |
| 5 | 3/6/2013 | 1 | 1 | 1 | 25 | 189 |
+--------+---------------+----+----+----+------+-----------+
And instead, I want to setup my table like:
+----------+---------------+---------------+-----------+
| pkid | DateOfService | ServiceTypeID | CLIENT_ID |
+----------+---------------+---------------+-----------+
| 1 | 3/4/2013 | 1 | 123 |
| 2 | 3/5/2013 | 1 | 123 |
| 3 | 3/6/2013 | 1 | 123 |
| 4 | 3/5/2013 | 2 | 147 |
| 5 | 3/5/2013 | 3 | 147 |
| 6 | 3/6/2013 | 1 | 189 |
| 7 | 3/6/2013 | 2 | 189 |
| 8 | 3/6/2013 | 3 | 189 |
+----------+---------------+---------------+-----------+
The ServiceTypeID table would be an options table setup something like:
+-------------------+---------+
| ServiceTypeID | Service |
+-------------------+---------+
| 1 | AM |
| 2 | MD |
| 3 | PM |
+-------------------+---------+
I need help coming up with a query I can run that will select and loop over the existing data and populate my new table.
You could use the UNPIVOT function to turn your current table from columns into rows. Then you can join on your options table and insert the data into your new table.
The UNPIVOT code will be:
select id,
dateofservice,
client_id,
col,
value
from yourtable
unpivot
(
value
for col in (AM, MD, PM)
) unpiv
where value <> 0
See SQL Fiddle with Demo
Then you will join to your options table to get the result. You can use this query to INSERT INTO your new table:
-- INSERT INTO yourNewTable
select src.id as pkid,
src.dateofservice,
o.servicetypeid,
src.client_id
from
(
select id,
dateofservice,
client_id,
col,
value
from yourtable
unpivot
(
value
for col in (AM, MD, PM)
) unpiv
where value <> 0
) src
inner join options o
on src.col = o.service
See SQL Fiddle with Demo
Since your options table is "setup", something like this would work:
INSERT INTO newtable
SELECT id, dateofservice, 1, clientid
FROM oldTable
WHERE AM = 1
UNION ALL
SELECT id, dateofservice, 2, clientid
FROM oldTable
WHERE MD = 1
UNION ALL
SELECT id, dateofservice, 3, clientid
FROM oldTable
WHERE PM = 1
NB, I'm making the same assumption blue is, your example table is wrong and pk should have had the values 1,2,3,4,4,5,5,5
If you want that key to be unique then just define it as an auto increment key and don't include it in this select.