Copy data from same table based on other columns - sql-server

I have a table named tableX structed like this. Notice the relation of childNo and parentNo columns.
childNo | parentNo |locationId |value
--------+----------+-----------+--------+
26 | NULL | 7 | value1 |
27 | NULL | 7 | value2 |
28 | 27 | 7 | value3 |
29 | 27 | 7 | value4 |
30 | 27 | 7 | value5 |
34 | NULL | 7 | value6 |
and I want to copy the same value column information to a different locationId(let's say locationId = 3) with new childNo and parentNo (protecting the relation)
Expected output.
childNo | parentNo |locationId |value
--------+----------+-----------+--------+
36 | NULL | 3 | value1 |
37 | NULL | 3 | value2 |
38 | 37 | 3 | value3 |
39 | 37 | 3 | value4 |
40 | 37 | 3 | value5 |
44 | NULL | 3 | value6 |
How can I achieve that using T-SQL?

Perhaps I'm missing something, but you could just do:
insert into t(childNo, parentNo, locationId, value)
select t.childNo + 10, t.ParentNo + 10, 3, t.value
from t
where t.locationId = 7;

I try this sample in SQL Server 2012.
DECLARE #lastParent int;
DECLARE #lastChild int;
DECLARE #newLocationId int;
SET #newLocationId = 3;
SELECT #lastParent = MAX(ParentNo) FROM tableX WHERE ParentNo IS NOT NULL;
SELECT #lastChild = MAX(ChildNo) from tableX;
INSERT INTO tableX (childNo, parentNo , LocationId, valueColumn)
SELECT
#lastChild + ROW_NUMBER() OVER (PARTITION BY LocationId ORDER BY childNo) AS childNo,
CASE WHEN (ParentNo IS NOT NULL) then #lastParent + 1
ELSE ParentNo
END as NewParentNo,
#NewLocationId as NewLocationId, valueColumn
FROM tableX
WHERE LocationId = 7
ORDER BY childNo
Two instances of this code running at same time, can produce duplicates values. I think your table must have Unique constraint on childNo and some trigger to validate parentNo

Related

1 Table, 2 Column (int, varchar), 1 NEW Key as int

Could use your help on this.
I have a table with 2 columns like this:
Column 1 | Column 2
---------+--------------------
1 | ( 1, 1,1)
2 | ( 45, 2,9)
3 | ( 4456, 98,0)
4 | ( 196416,511,1)
97 | ( 3213658, 45,7)
98 | ( 2315, 6,7)
99 | (9999999999,999,9)
AS definition:
Column 1: max 2 characters
Column 2 - Part 1 max 10 characters
Column 2 - Part 2 max 3 characters
Column 2 - Part 3 max 1 characters
So the key has to be at least 16 characters long.
In the end I need help in creating the key for column 3 as int - looking like this:
Column 1 | Column 2 | Column 3 - Key
---------+--------------------+------------------
1 | ( 1, 1,1) | 0100000000010011
2 | ( 45, 2,9) | 0200000000450029
3 | ( 4456, 98,0) | 0300000044560980
4 | ( 196416,511,1) | 0400001964165111
97 | ( 3213658, 45,7) | 9700032136580457
98 | ( 2315, 6,7) | 9800000023150067
99 | (9999999999,999,9) | 9999999999999999
Would appreciate your help.
Thanks and Regards,
Erik
You were told already, that this storage is NOT recommended. Rather use 3 separate columns for this and compute the needed value on the fly when needed.
As this seems to be padded correctly it might be as simple as this:
DECLARE #tbl TABLE(Column1 INT, Column2 VARCHAR(1000));
INSERT INTO #tbl VALUES
( 1,'( 1, 1,1)')
,( 2,'( 45, 2,9)')
,( 3,'( 4456, 98,0)')
,( 4,'( 196416,511,1)')
,(97,'( 3213658, 45,7)')
,(98,'( 2315, 6,7)')
,(99,'(9999999999,999,9)');
SELECT *
,REPLACE(REPLACE(REPLACE(REPLACE(t.Column2,' ','0'),',',''),'(',''),')','')
FROM #tbl t;
Alternatively you can split this Column 2 and deal with the values separately:
--using the JSON-splitting here (needs v2016+)
--Below v2016 there are various splitting approaches (XML based, Jeff Moden's function and many more...)
SELECT t.*
,A.*
,CONCAT(REPLACE(STR(A.val1,10),' ','0')
,REPLACE(STR(A.val2, 3),' ','0')
,REPLACE(STR(A.val3, 1),' ','0')) AS [new key]
FROM #tbl t
CROSS APPLY OPENJSON(REPLACE(REPLACE(t.Column2,'(','[['),')',']]'))
WITH(val1 BIGINT '$[0]'
,val2 BIGINT '$[1]'
,val3 BIGINT '$[2]') A;
The result
+---------+--------------------+------------+------+------+--------------------+
| Column1 | Column2 | val1 | val2 | val3 | new key |
+---------+--------------------+------------+------+------+--------------------+
| 1 | ( 1, 1,1) | 1 | 1 | 1 | 00000000010011 |
+---------+--------------------+------------+------+------+--------------------+
| 2 | ( 45, 2,9) | 45 | 2 | 9 | 00000000450029 |
+---------+--------------------+------------+------+------+--------------------+
| 3 | ( 4456, 98,0) | 4456 | 98 | 0 | 00000044560980 |
+---------+--------------------+------------+------+------+--------------------+
| 4 | ( 196416,511,1) | 196416 | 511 | 1 | 00001964165111 |
+---------+--------------------+------------+------+------+--------------------+
| 97 | ( 3213658, 45,7) | 3213658 | 45 | 7 | 00032136580457 |
+---------+--------------------+------------+------+------+--------------------+
| 98 | ( 2315, 6,7) | 2315 | 6 | 7 | 00000023150067 |
+---------+--------------------+------------+------+------+--------------------+
| 99 | (9999999999,999,9) | 9999999999 | 999 | 9 | 99999999999999 |
+---------+--------------------+------------+------+------+--------------------+
update if below v2016
Before JSON one often used hack was something along this:
SELECT t.*
,A.*
,CONCAT(REPLACE(STR(A.AsXml.value('/x[1]','bigint'),10),' ','0')
,REPLACE(STR(A.AsXml.value('/x[2]','bigint'), 3),' ','0')
,REPLACE(STR(A.AsXml.value('/x[3]','bigint'), 1),' ','0')) AS [new key]
FROM #tbl t
CROSS APPLY(SELECT CAST(REPLACE(REPLACE(REPLACE(t.Column2,',','</x><x>'),'(','<x>'),')','</x>') AS XML)) A(AsXml);
Hint: In any case the result is a string and not a number. Otherwise you would not keep the leading zeros... But you might use CAST(... AS BIGINT) to achieve a key as (big) integer number.

T-SQL Limited Cross Join

I want to join 2 tables such that I get the NAR for every combination of Type and BillingID where it exists.
Where a BillingID doesn't have a certain Type, then either NULL or 0 is returned for the NAR along with the Type and BillingID.
Is something like this even possible using SQL?
A simplified version of my data is shown below:
Type list:
+----------+
| Type |
+----------+
| NEW |
| CHNG |
| LAP |
+----------+
Data:
+----------+-----------+-----+
| Type | BillingID | NAR |
+----------+-----------+-----+
| NEW | ABC | 5 |
| CHNG | ABC | 15 |
| LAP | ABC | 10 |
| CHNG | DEF | 20 |
+----------+-----------+-----+
Desired result:
+----------+-----------+-----+
| Type | BillingID | NAR |
+----------+-----------+-----+
| NEW | ABC | 5 |
| CHNG | ABC | 15 |
| LAP | ABC | 10 |
| CHNG | DEF | 20 |
| NEW | DEF | 0 |
| LAP | DEF | 0 |
+----------+-----------+-----+
The last 2 rows are what is causing me problems.
I think you can do it like this:
declare #table table (type1 varchar(5))
insert into #table
values
('new'),
('chng'),
('lap')
declare #table2 table (typeid varchar(5),billingid varchar(5),nar int)
insert into #table2
values
( 'NEW', 'ABC', 5 ),
( 'CHNG' , 'ABC', 15 ),
( 'LAP' , 'ABC', 10 ),
( 'CHNG' , 'DEF', 20 )
select Z.*,case when c.nar IS null then 0 else c.nar end as nar from (
select * from #table a
outer apply (select distinct billingid from #table2 b ) p
)Z
left join #table2 c on Z.type1 = c.typeid and Z.billingid = c.billingid
order by billingid
Result

TSQL-JOIN: display non-existing values in one table as zero in Join

I have two tables:
ProjectProduct (two IDs for project and product) and
ProductStatus (which contains quality ratings for products).
Table: ProjectProduct
| productID | projectID |
| --------- | --------- |
| 39 | 26 |
| 40 | 26 |
| 41 | 26 |
| 42 | 26 |
Table: ProductStatus
| id | projectID | productID | typeID | Status | Comment | RatedBy |
| --- | --------- | --------- | ------ | ------ | ------------ | ------- |
| 1 | 26 | 39 | 2 | 30 | comm1 | 0 |
| 2 | 26 | 39 | 2 | 70 | comm2 | 0 |
| 22 | 26 | 39 | 1 | 70 | diff type | 0 |
| 3 | 26 | 39 | 2 | 100 | comm3 | 0 |
| 4 | 26 | 39 | 2 | 70 | diff ratedBy | 1 |
| 5 | 26 | 39 | 2 | 100 | comm5 | 0 |
| 6 | 26 | 39 | 2 | 30 | comm6 | 0 |
I need to join those two tables so that every product in the ProjectProduct table will display a Status value even if there is no corosponding entry in the ProductStatus table.
In above example only productID 39 exists in both tables. To show all productIDs the output should contain a zero value in the Status column for productIDs 40 to 42.
This is the part that I cannot figure out. For example the ProductStatus TypeID can contain 1 or 2. TypeID 2 has to be distinguished between RatedBy which can contain 0 or 1. Do I need the table or select statement to reflect all possible permutations?
Also only the row with the highest ID grouped by projectID, productID, typeID, RatedBy should be picked where there is an existing rating.
Desired output:
I also created a SQL Fiddle
I hope you can guide me in the right direction.
Thank you.
Query
SELECT ProjectProduct.projectID, ProjectProduct.productID, ProductStatus.typeid,ProductStatus.status,ProductStatus.Comment,ProductStatus.ratedBY
FROM ProjectProduct
left join
(
SELECT max(ID) id, productID,projectID
FROM ProductStatus
where typeID = 2
and ratedBY = 0
group BY productID,projectID ) maxproduct on ProjectProduct.productID = maxproduct.productID and ProjectProduct.projectID = maxproduct.projectID
left join ProductStatus on ProductStatus.id = maxproduct.ID
Output
you can use temp table like that:
select Max(Id) maxID ,ProductId,ProjectId into #Temp1 from ProductStatus
where typeID=2 and RatedBy=0
group by ProductId,ProjectId
select * into #Temp2 from ProductStatus where Id In (select MaxId from #Temp1)
select p.ProjectId,p.ProductId, 2 as typeID,isnull(t.status,0) as Status,t.Comment,0 as RatedBy
from ProjectProduct p left join #Temp2 t on p.ProductId=t.ProductId and p.ProjectId=t.ProjectId

Getting values from a table that's inside a table (unpivot / cross apply)

I'm having a serious problem with one of my import tables. I've imported an Excel file to a SQL Server table. The table ImportExcelFile now looks like this (simplified):
+----------+-------------------+-----------+------------+--------+--------+-----+---------+
| ImportId | Excelfile | SheetName | Field1 | Field2 | Field3 | ... | Field10 |
+----------+-------------------+-----------+------------+--------+--------+-----+---------+
| 1 | C:\Temp\Test.xlsx | Sheet1 | Age / Year | 2010 | 2011 | | 2018 |
| 2 | C:\Temp\Test.xlsx | Sheet1 | 0 | Value1 | Value2 | | Value9 |
| 3 | C:\Temp\Test.xlsx | Sheet1 | 1 | Value1 | Value2 | | Value9 |
| 4 | C:\Temp\Test.xlsx | Sheet1 | 2 | Value1 | Value2 | | Value9 |
| 5 | C:\Temp\Test.xlsx | Sheet1 | 3 | Value1 | Value2 | | Value9 |
| 6 | C:\Temp\Test.xlsx | Sheet1 | 4 | Value1 | Value2 | | Value9 |
| 7 | C:\Temp\Test.xlsx | Sheet1 | 5 | NULL | NULL | | NULL |
+----------+-------------------+-----------+------------+--------+--------+-----+---------+
I now want to insert those values from Field1 to Field10 to the table AgeYear(in my original table there are about 70 columns and 120 rows). The first row (Age / Year, 2010, 2011, ...) is the header row. The column Field1 is the leading column. I want to save the values in the following format:
+-----------+-----+------+--------+
| SheetName | Age | Year | Value |
+-----------+-----+------+--------+
| Sheet1 | 0 | 2010 | Value1 |
| Sheet1 | 0 | 2011 | Value2 |
| ... | ... | ... | ... |
| Sheet1 | 0 | 2018 | Value9 |
| Sheet1 | 1 | 2010 | Value1 |
| Sheet1 | 1 | 2011 | Value2 |
| ... | ... | ... | ... |
| Sheet1 | 1 | 2018 | Value9 |
| ... | ... | ... | ... |
+-----------+-----+------+--------+
I've tried the following query:
DECLARE #sql NVARCHAR(MAX) =
';WITH cte AS
(
SELECT i.SheetName,
ROW_NUMBER() OVER(PARTITION BY i.SheetName ORDER BY i.SheetName) AS rn,
' + #columns + ' -- #columns = 'Field1, Field2, Field3, Field4, ...'
FROM dbo.ImportExcelFile i
WHERE i.Sheetname LIKE ''Sheet1''
)
SELECT SheetName,
age Age,
y.[Year]
FROM cte
CROSS APPLY
(
SELECT Field1 age
FROM dbo.ImportExcelFile
WHERE SheetName LIKE ''Sheet1''
AND ISNUMERIC(Field1) = 1
) a (age)
UNPIVOT
(
[Year] FOR [Years] IN (' + #columns + ')
) y
WHERE rn = 1'
EXEC (#sql)
So far I'm getting the desired ages and years. My problem is that I don't know how I could get the values. With UNPIVOT I don't get the NULL values. Instead it fills the whole table with the same values even if they are NULL in the source table.
Could you please help me?
Perhaps an alternative approach. This is not dynamic, but with the help of a CROSS APPLY and a JOIN...
The drawback is that you'll have to define the 70 fields.
Example
;with cte0 as (
Select A.ImportId
,A.SheetName
,Age = A.Field1
,B.*
From ImportExcelFile A
Cross Apply ( values ('Field2',Field2)
,('Field3',Field3)
,('Field10',Field10)
) B (Item,Value)
)
,cte1 as ( Select * from cte0 where ImportId=1 )
Select A.SheetName
,[Age] = try_convert(int,A.Age)
,[Year] = try_convert(int,B.Value)
,[Value] = A.Value
From cte0 A
Join cte1 B on A.Item=B.Item
Where A.ImportId>1
Returns

Get the column name for a row where the column has the highest value

I got a table with 10 columns, the first 3 columns are used as identifiers and the other 7 columns contain numbers.
So my table structure looks like:
| ID1 | ID2 | ID3 | Data1 | Data2 | Data3 | Data4 | Data5 | Data6 | Data7 |
Now I need to get the column name (Data1 - Data7) of the column which has the highest value for the specific row.
So the data in the table may look like:
| A | B | C | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
| A | B | D | 5 | 8 | 9 | 3 | 51 | 11 | 4 |
| D | A | F | 31 | 5 | 9 | 11 | 23 | 2 | 9 |
The desired outcome for this input would be:
| A | B | C | Data7 |
| A | B | D | Data5 |
| D | A | F | Data1 |
I've tried this with an unpivot query and I can get a table which shows the data ( 7, 51 and 23 ) in the last column, but I can't add the column names to this due to the group by clause which is on ID1, ID2 and ID3. Adding the pivot field to this group by clause results in showing all the data unpivoted.
Use windowing functions (ROW_NUMBER) instead of the GROUP BY:
WITH Ordering AS
(
SELECT *, ROW_NUMBER() OVER (PARTITION BY ID1, ID2, ID3 ORDER BY Value DESC) AS OrderingValue
FROM table
UNPIVOT
(
Value FOR Source IN (Data1, Data2, Data3, Data4, Data5, Data6, Data7)
) up
)
SELECT *
FROM Ordering
WHERE OrderingValue = 1
Results:
ID1 ID2 ID3 Value Source OrderingValue
A B C 7 Data7 1
A B D 51 Data5 1
D A F 31 Data1 1
Obviously instead of SELECT * FROM Ordering, you can SELECT ID1, ID2, ID3, Source.

Resources