I have a question about SQL Server: how to combine three columns into one column with identify in a query.
Table: emp
empid | addr1 | addr2 | addr3
------+-------+-------+------
1 | hyd | chen | pune
2 | del | hyd | nagp
1 | pune | bang | keral
Based on above data I want output like below
empid | empaddrvalues | EmpFields | EmpFieldFlag
------+---------------+-----------+--------------
1 | hyd | addr1 | 1
1 | chen | addr2 | 2
1 | pune | addr3 | 3
2 | del | addr1 | 1
2 | hyd | addr2 | 2
2 | nagp | addr3 | 3
1 | pune | addr1 | 1
1 | bang | addr2 | 2
1 | keral | addr3 | 3
I tried with a query like this:
select
empid,
cast (addr1 as varchar(100)) as empaddrvalues,
'addr1' as EmpFields, '1' as EmpFieldFlag
from
emp
union all
select
empid,
cast (addr2 as varchar(100)) as empaddrvalues,
'addr2' as EmpFields, '2' as EmpFieldFlag
from
emp
union all
select
empid,
cast (addr3 as varchar(100)) as empaddrvalues,
'addr3' as EmpFields, '3' as EmpFieldFlag
from
emp
This query is returning the correct result, but it takes a lot of time due to calling same table 3 times.
Can you please tell me how to write any alternative query to achieve this task in SQL Server?
Unpivot by cross applying with 3 values (1, 2, 3) and CASE on that value to get the corresponding address.
create table dbo.employee
(
empid int,
addr1 varchar(50),
addr2 varchar(50),
addr3 varchar(50)
)
go
insert into dbo.employee(empid, addr1, addr2, addr3)
values
(1, 'hyd', 'chen', 'pune'),
(2, 'del', 'hyd', 'nagp'),
(1, 'pune', 'bang', 'keral');
go
select
e.empid,
case nums.n when 1 then addr1 when 2 then addr2 when 3 then addr3 end as empaddrvalues,
--concat('???', nums.n) as xyz,
nums.n as EmpFieldFlag
from dbo.employee as e
cross apply (values(1), (2), (3)) as nums(n);
go
drop table dbo.employee
go
Related
Course
+-----+----------+
| id | c_name |
+-----+----------+
| 1 | course1 |
| 7 | course2 |
+-----+----------+
Chapter
+-----+----------+------------+
| id | Ch_name | c_id |
+-----+----------+------------+
| 3 | Chapter1 | 1 |
| 9 | Chapter2 | 7 |
| 11 | Chapter3 | 1 |
| 17 | Chapter4 | 1 |
+-----+----------+------------+
I'm trying to select all data so that I can generate the following output:
+-----+-- |
|Course |
+-----+-- |
|Course1 |
|Chapter1 |
|Chapter3 |
|Chapter4 |
| |
|Course2 |
|Chapter2 |
I have tried in this way:
select
c.CourseID ,
'Course' as table_name,
c.CourseName as Course,
'' as Chapter
from [MstCourse]c
union
select
s.CourseID,
'Chapter' as table_name,
c.CourseName as Course,
s.ChapterName as Chapter
from [MstCourse] c
inner JOIN [ManageChapter] s ON c.CourseID= s.CourseID
order by Course, Chapter
But I am not getting the results in a single column.
You could achieve this with a group by ... with rollup clause.
Sample data
create table course
(
id int,
name nvarchar(10)
);
insert into course(id, name) values
(1, 'Course1'),
(7, 'Course2');
create table chapter
(
id int,
name nvarchar(10),
c_id int
);
insert into chapter(id, name, c_id) values
(3 , 'Chapter1', 1),
(9 , 'Chapter2', 7),
(11, 'Chapter3', 1),
(17, 'Chapter4', 1);
Solution
select coalesce(ch.Name, co.Name) as [Course]
from course co
join chapter ch
on ch.c_id = co.id
group by co.Name, ch.Name with rollup
having grouping(co.Name) <> 1
order by co.Name, ch.Name;
For some background on how this solution works, have a look at this fiddle.
I have the following table:
EventValue | Person1 | Person2 | Person3 | Person4 | Meta1 | Meta2
-------------------------------------------------------------------------------------------
123 | joePerson01 | samRock01 | nancyDrew01 | steveRogers01 | 505 | 606
321 | steveRogers02 | yoMama01 | ruMo01 | lukeJedi01 | 707 | 808
I want to transform the Person columns into IDs for my destination table, so all of the ID's would be coming from the same Person table in my Destination DB:
ID | FirstName | LastName | DatabaseOneID | DatabaseTwoID
----------------------------------------------------------
1 | Joe | Person | joePerson01 | personJoe01
2 | Sam | Rockwell | samRock01 | rockSam01
3 | Nancy | Drew | nancyDrew01 | drewNancy01
4 | Steve | Rogers | steveRogers01 | rogersSteve01
5 | Steve R | Rogers | steveRogers02 | rogersSteve02
6 | Yo | Mama | yoMama01 | mamaYo01
7 | Rufus | Murdock | ruMo01 | moRu01
8 | Luke | Skywalker | lukeJedi01 | jediLuke01
With results like so:
MetaID | EventValue | Person1ID | Person2ID | Person3ID | Person4ID
------------------------------------------------------------------------
1 | 123 | 1 | 2 | 3 | 4
2 | 321 | 5 | 6 | 7 | 8
I currently have a Lookup Transform looking up the first Person column, but couldn't figure out how to convert all 4 Person columns into IDs within the same lookup.
You could do it in one query, or use UNPIVOT, or use a scalar function if you think it'll be more fixable for your implementation. Then, you just create a view of it, in which it'll be an easy access for you.
here is a quick example :
DECLARE
#tb1 TABLE
(
EventValue INT
, Person1 VARCHAR(250)
, Person2 VARCHAR(250)
, Person3 VARCHAR(250)
, Person4 VARCHAR(250)
, Meta1 INT
, Meta2 INT
)
DECLARE
#Person TABLE
(
ID INT
, FirstName VARCHAR(250)
, LastName VARCHAR(250)
, DatabaseOneID VARCHAR(250)
, DatabaseTwoID VARCHAR(250)
)
INSERT INTO #tb1
VALUES
(123,'joePerson01','samRock01','nancyDrew01','steveRogers01',505,606),
(321,'steveRogers02','yoMama01','ruMo01','lukeJedi01',707,808)
INSERT INTO #Person
VALUES
(1,'Joe','Person','joePerson01','personJoe01'),
(2,'Sam','Rockwell','samRock01','rockSam01'),
(3,'Nancy','Drew','nancyDrew01','drewNancy01'),
(4,'Steve','Rogers','steveRogers01','rogersSteve01'),
(5,'SteveR','Rogers','steveRogers02','rogersSteve02'),
(6,'Yo','Mama','yoMama01','mamaYo01'),
(7,'Rufus','Murdock','ruMo01','moRu01'),
(8,'Luke','Skywalker','lukeJedi01','jediLuke01')
SELECT ROW_NUMBER() OVER(ORDER BY EventValue) AS MetaID, *
FROM (
SELECT
t.EventValue
, MAX(CASE WHEN t.Person1 IN(p.DatabaseOneID, p.DatabaseTwoID) THEN p.ID ELSE NULL END) AS Person1ID
, MAX(CASE WHEN t.Person2 IN(p.DatabaseOneID, p.DatabaseTwoID) THEN p.ID ELSE NULL END) AS Person2ID
, MAX(CASE WHEN t.Person3 IN(p.DatabaseOneID, p.DatabaseTwoID) THEN p.ID ELSE NULL END) AS Person3ID
, MAX(CASE WHEN t.Person4 IN(p.DatabaseOneID, p.DatabaseTwoID) THEN p.ID ELSE NULL END) AS Person4ID
FROM #tb1 t
LEFT JOIN #Person p
ON p.DatabaseOneID IN(t.Person1, t.Person2, t.Person3, t.Person4)
OR p.DatabaseTwoID IN(t.Person1, t.Person2, t.Person3, t.Person4)
GROUP BY t.EventValue
) D
I currently have a Lookup Transform looking up the first Person column, but couldn't figure out how to convert all 4 Person columns into IDs within the same lookup.
You cannot do this within the same lookup, you have to add a Lookup Transformation for each Column. In your case you should add 4 Lookup Transformation.
If source database and destination database are on the same server, then you can use a SQL query to achieve that as mentioned in the other answer, but in case that each database is on a separate server you have to go with Lookup transformation or you have to import data into a staging table and perform Join operations using SQL.
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?
I have an Address table that contains multiple addresses
Address:
| Id | details | ... |
- - - - - - - - - - - - - - - -
| 1 | details | ... |
| 2 | details | ... |
| 3 | details | ... |
| 4 | details | ... |
Then i have a site table, that contains address.id as FK to represent Billing or Shipping address,
Site:
| Id | details | Ship_Address_id | Bill_Address_id | ... |
- - - - - - - - - - - - - - - -
| 1 | details | 1 | 2 | |
| 2 | details | 1 | 3 | |
| 3 | details | 4 | 4 | |
| 4 | details | 2 | 3 | |
is there a way to join both the tables so that single row from site table may fetch two rows of address table, even in the case of same address of both columns.
i was expecting to use multiple join of site with address but that is not working:
This is what i tried:
SELECT CASE
WHEN ship.CRM_Address_Internal_Id_Ship IS NOT NULL THEN 'Ship'
WHEN bill.CRM_Address_Internal_Id_Ship IS NOT NULL THEN 'Bill'
ELSE '' END
FROM Address as Adr
LEFT JOIN Site ship ON ship.Ship_Address_id = Adr.id
LEFT JOIN Site bill ON bill.Bill_Address_id = Adr.id
Even if i don't use the CASE it does not fetch two rows for each address with respect to site.
Please advise.
EDIT
Here is the desired output table:
as in Site table we have two different ids of address table (Ship/Bill) so the result table should display two rows for each site.id
for example i'm fetching records for site.id 1 and 3 then
Result_Table:
| Address.ID | Address Details | TYPE | ... |
--------------------------------------------
| 1 | Other Details | Ship | ... |
| 2 | Other Details | Bill | ... |
| 4 | Other Details | Ship | ... |
| 4 | Other Details | Bill | ... |
There are various ways to do this. This is an example that I implemented by UNPIVOT, you can take a look as references:
declare #address table(id int, details nvarchar(100))
declare #site table(id int, details nvarchar(100), ship_address_id int, bill_address_id int)
insert into #address values(1,'details 1')
insert into #address values(2,'details 2')
insert into #address values(3,'details 3')
insert into #address values(4,'details 4')
insert into #site values(1,'details 1',1,2)
insert into #site values(2,'details 1',1,3)
insert into #site values(3,'details 1',4,4)
insert into #site values(4,'details 1',2,5)
SELECT *
FROM (
SELECT s.id as site_id, s.bill_address_id, s.ship_address_id
FROM #Site AS s
INNER JOIN #Address AS sa ON s.Ship_Address_id = sa.Id
INNER JOIN #Address AS ba ON s.Bill_Address_id = ba.Id
WHERE s.id in (1,3)) p
UNPIVOT
(address_id FOR address_type IN
(Ship_Address_id, Bill_Address_id)
) AS unpvt
Output
site_id address_id address_type
1 1 ship_address_id
1 2 bill_address_id
3 4 ship_address_id
3 4 bill_address_id
It looks as though you're looking for UNION ALL, and also that your tables are backwards. I think you're looking for something more like this:
SELECT ship.id Site_id, CASE
WHEN ship.CRM_Address_Internal_Id_Ship IS NOT NULL THEN 'Ship'
ELSE 'No Ship' END AddressPresent
FROM Site ship
LEFT JOIN Address Adr ON ship.Ship_Address_id = Adr.id
UNION ALL
SELECT bill.id Site_id, CASE
WHEN bill.CRM_Address_Internal_Id_Ship IS NOT NULL THEN 'Bill'
ELSE 'No Bill' END AddressPresent
FROM Site bill
LEFT JOIN Address Adr ON bill.Bill_Address_id = Adr.id
I'm also wondering if you have a 'CRM_Address_Internal_Id_Bill' column that should be in the second part of the query?
I am moving a bunch of code over from entirely cursor based to set based and generating this has been doing my head in. We create a 6 character shortcode (unique) for each company inserted into the database and I (want) to achieve this outside of a cursor.
Example of where I am at so far:
CREATE TABLE #customers (name VARCHAR(50), shortname VARCHAR(10))
INSERT INTO #customers VALUES
('Michael Smith', 'Michae')
,('Michael Douglas', 'Mich_1')
,('Michael Yang', 'Mich_2')
CREATE TABLE #newcustomers (name VARCHAR(50), shortname VARCHAR(10) NULL)
INSERT INTO #newcustomers (name) VALUES
('Michael Black')
,('Michael White')
SELECT * FROM #customers
SELECT * FROM #newcustomers
DECLARE #shortname VARCHAR(10)
DECLARE #iteration INT = 0
WHILE EXISTS(SELECT shortname FROM #customers WHERE shortname = #shortname)
BEGIN
SELECT #shortname = LEFT(name, 6) FROM #newcustomers
UPDATE #newcustomers SET shortname = #shortname
SET #shortname = LEFT(#shortname, 4) + '_' + #iteration
SET #iteration = #iteration + 1
END
Hopefully the example is sufficient in identifying where I am trying to get to, any suggestions or examples would be very helpful. My example does not work.
Try this
Your table as mock-up
CREATE TABLE #customers (ID INT IDENTITY, name VARCHAR(50), shortname VARCHAR(10))
INSERT INTO #customers VALUES
('Michael Smith', 'Michae')
,('Michael Douglas', 'Mich_1')
,('Michael Yang', 'Mich_3')
,('Testman', 'Testma')
,('Testman1', 'Test_1');
CREATE TABLE #newcustomers (ID INT IDENTITY,name VARCHAR(50), shortname VARCHAR(10) NULL)
INSERT INTO #newcustomers (name) VALUES
('Michael Black')
,('Michael White')
,('Testman2')
,('Someone new');
--This CTE will combine all existing names
WITH AllNames AS
(
SELECT '1_old' AS datasource,ID,name,shortname FROM #customers
UNION ALL SELECT '2_new',ID,name,shortname FROM #newcustomers
)
--This CTE will use the combined list and calculate the right "index"
,ShortNames AS
(
SELECT c.*
,A.First6
,ROW_NUMBER() OVER(PARTITION BY A.First6 ORDER BY datasource,ID) AS NrTotal
,ROW_NUMBER() OVER(PARTITION BY datasource,A.First6 ORDER BY datasource,ID) AS Nr
,CASE WHEN ISNUMERIC(SUBSTRING(shortname+' ',6,10))=1
THEN CAST(SUBSTRING(shortname+' ',6,10) AS INT) ELSE 0 END AS ExistIndex
FROM AllNames AS c
CROSS APPLY(SELECT LEFT(name + ' ',6)) AS A(First6)
)
--All new with NrTotal=1 get the 6 letters as is, all other get the index
SELECT *
,CASE WHEN datasource='1_old' THEN shortname ELSE
CASE WHEN datasource='2_new' AND NrTotal=1 THEN First6
ELSE LEFT(First6,4) + '_' + CAST(Nr + (SELECT ISNULL(MAX(x.ExistIndex),1)
FROM ShortNames AS x
WHERE x.First6=ShortNames.First6) AS VARCHAR(5))
END
END
FROM ShortNames
GO
DROP TABLE #customers;
DROP TABLE #newcustomers;
The result
+------------+----+-----------------+-----------+--------+---------+----+------------+--------------------+
| datasource | ID | name | shortname | First6 | NrTotal | Nr | ExistIndex | (Kein Spaltenname) |
+------------+----+-----------------+-----------+--------+---------+----+------------+--------------------+
| 1_old | 1 | Michael Smith | Michae | Michae | 1 | 1 | 0 | Michae |
+------------+----+-----------------+-----------+--------+---------+----+------------+--------------------+
| 1_old | 2 | Michael Douglas | Mich_1 | Michae | 2 | 2 | 1 | Mich_1 |
+------------+----+-----------------+-----------+--------+---------+----+------------+--------------------+
| 1_old | 3 | Michael Yang | Mich_3 | Michae | 3 | 3 | 3 | Mich_3 |
+------------+----+-----------------+-----------+--------+---------+----+------------+--------------------+
| 1_old | 4 | Testman | Testma | Testma | 1 | 1 | 0 | Testma |
+------------+----+-----------------+-----------+--------+---------+----+------------+--------------------+
| 1_old | 5 | Testman1 | Test_1 | Testma | 2 | 2 | 1 | Test_1 |
+------------+----+-----------------+-----------+--------+---------+----+------------+--------------------+
| 2_new | 1 | Michael Black | NULL | Michae | 4 | 1 | 0 | Mich_4 |
+------------+----+-----------------+-----------+--------+---------+----+------------+--------------------+
| 2_new | 2 | Michael White | NULL | Michae | 5 | 2 | 0 | Mich_5 |
+------------+----+-----------------+-----------+--------+---------+----+------------+--------------------+
| 2_new | 4 | Someone new | NULL | Someon | 1 | 1 | 0 | Someon |
+------------+----+-----------------+-----------+--------+---------+----+------------+--------------------+
| 2_new | 3 | Testman2 | NULL | Testma | 3 | 1 | 0 | Test_2 |
+------------+----+-----------------+-----------+--------+---------+----+------------+--------------------+
One option is to use a computed column.
A table design along these lines would work:
- Sample table.
DECLARE #Sample TABLE
(
Id INT IDENTITY(1, 1),
FullName VARCHAR(255),
ShortName AS LEFT(FullName, 4) + '_' + CAST(Id AS VARCHAR(255))
)
;
-- Add set containing two companies.
INSERT INTO #Sample
(
FullName
)
VALUES
('ABC LTD'),
('XYZ PLC')
;
Returns
Id FullName ShortName
1 ABC LTD ABC _1
2 XYZ PLC XYZ _1
The Id and ShortName columns will be managed by SQL Server. You only need to add the FullName.
EDIT
Reworked example using table variable, to make it easier to play along.