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

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.

Related

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

SQL Dynamic Pivoting with Dynamic Data

I am using an SQL Server database and have these following tables
Table "Data"
------------------
| Id | data_name |
------------------
| 1 |Data 1 |
| 2 |Data 2 |
| 3 |Data 3 |
| 4 |Data 4 |
| 5 |Data 5 |
------------------
and Table "Value_data"
--------------------------------------------------------------------------------------------------------------
| Id | data_id | date | col_1_type | col_1_name | col_1_value | col_2_type | col_2_name | col_2_value |
--------------------------------------------------------------------------------------------------------------
| 1 | 1 | 2017-01-01 | A | Alpha | 12 | B | Beta | 23 |
| 2 | 1 | 2017-02-01 | A | Alpha | 32 | B | Beta | 42 |
---------------------------------------------------------------------------------------------------------------
And i want to make result like so
-----------------------------------------------------------------
|value_id | data_id | data_name | date | A-Alpha | B-Beta |
-----------------------------------------------------------------
|1 | 1 | Data 1 | 2017-01-01 | 12 | 23 |
|2 | 1 | Data 1 | 2017-02-01 | 32 | 42 |
-----------------------------------------------------------------
I've search multiple times for solutions, i've tried using Pivot for example for a static result,
DECLARE #Data TABLE ( Id INT, data_name VARCHAR(10) )
INSERT INTO #Data VALUES
( 1 ,'Data 1'),
( 2 ,'Data 2'),
( 3 ,'Data 3'),
( 4 ,'Data 4'),
( 5 ,'Data 5')
DECLARE #Value_data TABLE (Id INT, data_id INT, [date] DATE, col_1_type VARCHAR(10), col_1_name VARCHAR(10), col_1_value INT, col_2_type VARCHAR(10), col_2_name VARCHAR(10), col_2_value INT)
INSERT INTO #Value_data VALUES
( 1, 1, '2017-01-01','A','Alpha','12','B','Beta','23'),
( 2, 1, '2017-02-01','A','Alpha','32','B','Beta','42')
;WITH CTE AS (
select vd.Id value_id
, vd.data_id
, d.data_name
, vd.[date]
, vd.col_1_type + '-' +vd.col_1_name Col1
, vd.col_1_value
, vd.col_2_type + '-' +vd.col_2_name Col2
, vd.col_2_value
from #Value_data vd
inner join #Data d on vd.data_id = d.Id
)
SELECT * FROM CTE
PIVOT( MAX(col_1_value) FOR Col1 IN ([A-Alpha])) PVT_A
PIVOT( MAX(col_2_value) FOR Col2 IN ([B-Beta])) PVT_B
but it wont work well with the data that i'm using with the joining tables because my database will be dynamic, anyone had a solution with the same case?

Copy data from same table based on other columns

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

Create Tree Query From Numeric Mapping Table in SQL (Specific Format)

I have an exported table from accounting software like below.
AccountID AccountName
--------- -----------
11 Acc11
12 Acc12
13 Acc13
11/11 Acc11/11
11/12 Acc11/12
11/111 Acc11/111
11/11/001 Acc11/11/001
11/11/002 Acc11/11/002
12/111 Acc12/111
12/112 Acc12/112
I want to convert it to tree query in MS-SQL Server 2008 to use it as a Treelist datasource in my win aaplication.
I raised this question before and it's answered with a way that it was very very slow for my big table with more than 5000 records (Create Tree Query From Numeric Mapping Table in SQL). But I think counting "/" and separating AccountID field with "/" can solve my problem easier and very faster.
Anyway, My expected result must be like below:
AccountID AccountName ID ParentID Level HasChild
--------- ----------- --- --------- ------ --------
11 Acc11 1 Null 1 1
12 Acc12 2 Null 1 1
13 Acc13 3 Null 1 0
11/11 Acc11/11 4 1 2 1
11/12 Acc11/12 5 1 2 0
11/111 Acc11/111 6 1 2 0
11/11/001 Acc11/11/001 7 4 3 0
11/11/002 Acc11/11/002 8 4 3 0
12/111 Acc12/111 9 2 2 0
12/112 Acc12/112 10 2 2 0
Please Help Me.
I modified my answer given in the first question...
It would be best, if your table would keep the relation data directly in indexed columns. Before you change your table's structure you might try this:
A table with test data
DECLARE #tbl TABLE ( AccountID VARCHAR(100), AccountName VARCHAR(100));
INSERT INTO #tbl VALUES
('11','Acc11')
,('12','Acc12')
,('13','Acc13')
,('11/11','Acc11/11')
,('11/12','Acc11/12')
,('11/111','Acc11/111')
,('11/11/001','Acc11/11/001')
,('11/11/002','Acc11/11/002')
,('12/111','Acc12/111')
,('12/112','Acc12/112');
This will get the needed data into a newly created temp table called #tempHierarchy
SELECT AccountID
,AccountName
,ROW_NUMBER() OVER(ORDER BY LEN(AccountID)-LEN(REPLACE(AccountID,'/','')),AccountID) AS ID
,Extended.HierarchyLevel
,STUFF(
(
SELECT '/' + A.B.value('.','varchar(10)')
FROM Extended.IDsXML.nodes('/x[position() <= sql:column("HierarchyLevel")]') AS A(B)
FOR XML PATH('')
),1,2,'') AS ParentPath
,Extended.IDsXML.value('/x[sql:column("HierarchyLevel")+1][1]','varchar(10)') AS ownID
,Extended.IDsXML.value('/x[sql:column("HierarchyLevel")][1]','varchar(10)') AS ancestorID
INTO #tempHierarchy
FROM #tbl
CROSS APPLY(SELECT LEN(AccountID)-LEN(REPLACE(AccountID,'/','')) + 1 AS HierarchyLevel
,CAST('<x></x><x>' + REPLACE(AccountID,'/','</x><x>') + '</x>' AS XML) AS IDsXML) AS Extended
;
The intermediate result
+-----------+--------------+----+----------------+------------+-------+------------+
| AccountID | AccountName | ID | HierarchyLevel | ParentPath | ownID | ancestorID |
+-----------+--------------+----+----------------+------------+-------+------------+
| 11 | Acc11 | 1 | 1 | | 11 | |
+-----------+--------------+----+----------------+------------+-------+------------+
| 12 | Acc12 | 2 | 1 | | 12 | |
+-----------+--------------+----+----------------+------------+-------+------------+
| 13 | Acc13 | 3 | 1 | | 13 | |
+-----------+--------------+----+----------------+------------+-------+------------+
| 11/11 | Acc11/11 | 4 | 2 | 11 | 11 | 11 |
+-----------+--------------+----+----------------+------------+-------+------------+
| 11/111 | Acc11/111 | 5 | 2 | 11 | 111 | 11 |
+-----------+--------------+----+----------------+------------+-------+------------+
| 11/12 | Acc11/12 | 6 | 2 | 11 | 12 | 11 |
+-----------+--------------+----+----------------+------------+-------+------------+
| 12/111 | Acc12/111 | 7 | 2 | 12 | 111 | 12 |
+-----------+--------------+----+----------------+------------+-------+------------+
| 12/112 | Acc12/112 | 8 | 2 | 12 | 112 | 12 |
+-----------+--------------+----+----------------+------------+-------+------------+
| 11/11/001 | Acc11/11/001 | 9 | 3 | 11/11 | 001 | 11 |
+-----------+--------------+----+----------------+------------+-------+------------+
| 11/11/002 | Acc11/11/002 | 10 | 3 | 11/11 | 002 | 11 |
+-----------+--------------+----+----------------+------------+-------+------------+
And now a similar recursive approach takes place as in my first answer. But - as it is using a real table now and all the string splitting has taken place already - it should be faster...
WITH RecursiveCTE AS
(
SELECT th.*
,CAST(NULL AS BIGINT) AS ParentID
,CASE WHEN EXISTS(SELECT 1 FROM #tempHierarchy AS x WHERE x.ParentPath=th.AccountID) THEN 1 ELSE 0 END AS HasChild
FROM #tempHierarchy AS th WHERE th.HierarchyLevel=1
UNION ALL
SELECT sa.AccountID
,sa.AccountName
,sa.ID
,sa.HierarchyLevel
,sa.ParentPath
,sa.ownID
,sa.ancestorID
,(SELECT x.ID FROM #tempHierarchy AS x WHERE x.AccountID=sa.ParentPath)
,CASE WHEN EXISTS(SELECT 1 FROM #tempHierarchy AS x WHERE x.ParentPath=sa.AccountID) THEN 1 ELSE 0 END AS HasChild
FROM RecursiveCTE AS r
INNER JOIN #tempHierarchy AS sa ON sa.HierarchyLevel=r.HierarchyLevel+1
AND r.AccountID=sa.ParentPath
)
SELECT r.AccountID
,r.AccountName
,r.ID
,r.ParentID
,r.HierarchyLevel
,r.HasChild
FROM RecursiveCTE AS r
ORDER BY HierarchyLevel,ParentID;
And finally I clean up
DROP TABLE #tempHierarchy;
And here's the final result
+-----------+--------------+----+----------+----------------+----------+
| AccountID | AccountName | ID | ParentID | HierarchyLevel | HasChild |
+-----------+--------------+----+----------+----------------+----------+
| 11 | Acc11 | 1 | NULL | 1 | 1 |
+-----------+--------------+----+----------+----------------+----------+
| 12 | Acc12 | 2 | NULL | 1 | 1 |
+-----------+--------------+----+----------+----------------+----------+
| 13 | Acc13 | 3 | NULL | 1 | 0 |
+-----------+--------------+----+----------+----------------+----------+
| 11/11 | Acc11/11 | 4 | 1 | 2 | 1 |
+-----------+--------------+----+----------+----------------+----------+
| 11/111 | Acc11/111 | 5 | 1 | 2 | 0 |
+-----------+--------------+----+----------+----------------+----------+
| 11/12 | Acc11/12 | 6 | 1 | 2 | 0 |
+-----------+--------------+----+----------+----------------+----------+
| 12/111 | Acc12/111 | 7 | 2 | 2 | 0 |
+-----------+--------------+----+----------+----------------+----------+
| 12/112 | Acc12/112 | 8 | 2 | 2 | 0 |
+-----------+--------------+----+----------+----------------+----------+
| 11/11/001 | Acc11/11/001 | 9 | 4 | 3 | 0 |
+-----------+--------------+----+----------+----------------+----------+
| 11/11/002 | Acc11/11/002 | 10 | 4 | 3 | 0 |
+-----------+--------------+----+----------+----------------+----------+

Sql query to check if a certain value appears more than once in rows

I have table with 5 columns like this
+----+-------------------------+-----------+--------+-----------+
| Id | CreateDate | CompanyId | UserId | IsEnabled |
+----+-------------------------+-----------+--------+-----------+
| 1 | 2016-01-02 23:40:46.517 | 1 | 1 | 1 |
| 2 | 2016-01-16 00:07:59.857 | 1 | 2 | 1 |
| 3 | 2016-01-25 15:17:54.420 | 3 | 3 | 1 |
| 25 | 2016-03-07 16:48:39.260 | 24 | 10 | 0 |
| 26 | 2016-03-07 16:48:39.263 | 25 | 2 | 0 |
+----+-------------------------+-----------+--------+-----------+
(thanks http://www.sensefulsolutions.com/2010/10/format-text-as-table.html for ASCII table!)
I'm trying to check if a UserId is recorded for more than one CompanyId's.
So far I managed to check if a UserId happens to appear more than one by using this query
WITH T AS
(
SELECT * ,
Count(*) OVER (PARTITION BY UserId) as Cnt
From CompanyUser
)
select Distinct UserId
FROM T
Where Cnt >1
It returns 2 correctly.
Where I'm stuck is, how can I parameterize the UserId and check if an Id is recorded for more than one company.
Declare #UserID as bigint
Set #UserID = 2
select Distinct Count(CompanyID)
FROM ComapynUser
Where UserId = #UserId
I think this gives you what you need.

Resources