SQL to fetch out dump data - sql

I kind of stuck in fetching out the count of unique customers I have in sqlserver table. The way table storing data is:
+----------+----------+----------+
| Value | Label | ClientID |
+----------+----------+----------+
| Mr | Title | 1 |
| Sul | Forename | 1 |
| Last | Surname | 1 |
| WD17 6JJ | Postcode | 1 |
+----------+----------+----------+
Now I have to count\list unique customer on the basis of forename,surname,postcode. Can someone please help

Here are two queries that will give you the desired results:
DECLARE #T table (Value varchar(255), Label varchar(255), ClientID int)
INSERT INTO #T
VALUES
('Mr', 'Title', 1)
, ('Sul', 'Forename', 1)
, ('Last', 'Surname', 1)
, ('WD17 6JJ', 'Postcode', 1)
, ('Dr', 'Title', 2) -- different Title will be ignored
, ('Sul', 'Forename', 2)
, ('Last', 'Surname', 2)
, ('WD17 6JJ', 'Postcode', 2)
, ('Mr', 'Title', 3)
, ('Sul2', 'Forename', 3) -- different Forename
, ('Last', 'Surname', 3)
, ('WD17 6JJ', 'Postcode', 3)
-- Using JOIN
SELECT DISTINCT
T1.Value Forename
, T2.Value Surname
, T3.Value Postcode
FROM
#T T1
JOIN #T T2 ON T1.ClientID = T2.ClientID AND T2.Label = 'Surname'
JOIN #T T3 ON T1.ClientID = T3.ClientID AND T3.Label = 'Postcode'
WHERE T1.Label = 'Forename'
-- Using PIVOT
SELECT DISTINCT
Forename
, Surname
, Postcode
FROM
(
SELECT
Value
, Label
, ClientID
FROM #T
) T
PIVOT
(
MAX (Value)
FOR Label IN
(
Forename, Surname, Postcode
)
) P

for a count of distinct Customers
SELECT COUNT (DISTINCT ClientId)
FROM dbo.table
Or use pivot to get a distinct list
DECLARE #Table TABLE ( [Value] NVARCHAR(20), Label NVARCHAR(20), ClientID INT)
INSERT INTO #Table
([Value], Label, ClientID)
VALUES
(N'Mr', N'Title', 1),
(N'Sul', N'Forename', 1),
(N'Last', N'Surname', 1),
(N'WD17 6JJ', N'Postcode', 1)
SELECT Pvt.Forename
, Pvt.Surname
, Pvt.Postcode
FROM #Table T
PIVOT ( MAX([Value]) FOR Label IN ([Title], [Forename], [Surname], [Postcode])) AS Pvt
GROUP BY
Pvt.Forename
,Pvt.Surname
,Pvt.Postcode

You can do something like this:
SELECT DISTINCT
id.clientID,
title.title,
Forename.Forename,
Surname.Surname,
Postcode.Postcode
FROM table id
CROSS APPLY (SELECT value AS title FROM table title WHERE label = 'title' AND title.clientID = id.clientID ) title
CROSS APPLY (SELECT value AS Forename FROM table Forename WHERE label = 'Forename' AND Forename.clientID = id.clientID ) Forename
CROSS APPLY (SELECT value AS Surname FROM table Surname WHERE label = 'Surname' AND Surname.clientID = id.clientID ) Surname
CROSS APPLY (SELECT value AS Postcode FROM table Postcode WHERE label = 'Postcode' AND Postcode.clientID = id.clientID ) Postcode
It's not pretty, but should work.

Related

Convert Table to Specific Column Wise

I have a table like this. How can I convert to this format?
DECLARE #A TaBLE (KeyValue INT, Name VARCHAR(50), Value VARCHAR(512))
INSERT INTO #A
VALUES (0, 'AccountID', '192507'), (0, 'member_id', '999159'),
(0, 'firstname', 'Test1'), (0, 'lastname', 'Last1'),
(1, 'AccountID', '192508'), (1, 'member_id', '999160'),
(1, 'firstname', 'Test2'), (1, 'lastname', 'Last2')
SELECT * FROM #A
I have table rows for this model:
KeyValue Name Value
-----------------------------------
0 AccountID 192507
0 member_id 999159
0 firstname Test1
0 lastname Last1
1 AccountID 192508
1 member_id 999160
1 firstname Test2
1 lastname Last2
My expected output is:
AccountID member_id firstname lastname
--------------------------------------------
192507 999159 Test1 Last1
192508 999160 Test2 Last2
I tried this code But it didn't work
select *
from
(
select Name,value
from #A
) d
pivot
(
MAX(value)
for Name in (AccountID,member_id,firstname,lastname)
) piv;
Try this below logic-
DEMO HERE
SELECT
MAX(CASE WHEN Name = 'AccountID' THEN Value ELSE NULL END) AccountID,
MAX(CASE WHEN Name = 'member_id' THEN Value ELSE NULL END) member_id ,
MAX(CASE WHEN Name = 'firstname' THEN Value ELSE NULL END) firstname ,
MAX(CASE WHEN Name = 'lastname' THEN Value ELSE NULL END) lastname
FROM #A
GROUP BY KeyValue
You can get the desired result by using PIVOT. In your query you just need to select all the columns, like below.
SELECT AccountID, member_id, firstname, lastname
FROM
(
select * from #A
) d
PIVOT
(
MAX(value)
FOR Name IN (AccountID, member_id, firstname, lastname)
) piv;
You can run the test here.
In the temp table, you should select all useful information like this
select AccountID, member_id, firstname, lastname
from
(
select * from #A -- instead of `select Name,value`
) d
pivot
(
MAX(value)
for Name in (AccountID,member_id,firstname,lastname)
) piv;
Result here

Get a row from one table and check the corresponding row in the other table if exists the add this row to the next of the first table row

I have a table say A which has some rows and 'Id' column as a primary key. And other table B and it has the 'TabAId' and references the table A id column.
Want to get a report like shown in the attached image.
Explanation
I 'sql server' database
select One row from the Table A and check the Id in the Table B, If exists then add table B row as next row(If multiple rows are there then also add those number of rows as next rows) else go further.
Tried with case statement which appends to the row, not adds as next row.
With join also happens same only.
It may be easy through programming language like php or scripting like jquery and ajax but I want it through sql server only. It helps me for the further requirements.
So please someone help me.
Edited:
create table tabA(id int not null primary key,
name varchar(20) null,age int null)
insert into tabA values(1,'Sudeep',35),
(2,'Darshan',34)
create table tabB(A_id int not null,nickname varchar(20) null )
insert into tabB values(1,'Kiccha'),
(1,'Nalla'),
(2,'Boss')
output should be like below
Id | name | age |
------------------------
1 | Sudeep | 35 |
------------------------
| *Kichha | |
------------------------
| *Nalla | |
------------------------
2 | Darshan | 34 |
------------------------
| *Boss | |
------------------------
based on the requirement i have done the following.
;
WITH cte
AS (
SELECT *
,DENSE_RANK() OVER (
ORDER BY id
) dn
,ROW_NUMBER() OVER (
PARTITION BY id ORDER BY age DESC
) rn
FROM (
SELECT *
FROM tabA a
UNION ALL
SELECT *
,NULL
FROM tabB b where exists (select 1 from taba a where a.id=b.A_id)
) a
)
SELECT iif(rn = 1, cast(id AS VARCHAR(50)), '') ID
,CONCAT (
iif(rn = 1, '', '*')
,name
) NAME
,iif(rn = 1, cast(age AS VARCHAR(50)), '') AGE
FROM cte
please let me know if anything needs to be added
Edit: as requested display the results based on the offset
if OBJECT_ID('tempdb..#cte_results') is not null
drop table #cte_results
/*
in order to achieve the second goal we need to store in results in a table then use that table to display
results
*/
;
WITH cte
AS (
SELECT *
,DENSE_RANK() OVER (
ORDER BY id
) dn
,ROW_NUMBER() OVER (
PARTITION BY id ORDER BY age DESC
) rn
,ROW_NUMBER() over( order by id asc,age desc) off_set
FROM (
SELECT *
FROM tabA a
UNION ALL
SELECT *
,NULL
FROM tabB b where exists (select 1 from taba a where a.id=b.A_id)
) a
)
SELECT iif(rn = 1, cast(id AS VARCHAR(50)), '') ID
,CONCAT (
iif(rn = 1, '', '*')
,name
) NAME
,iif(rn = 1, cast(age AS VARCHAR(50)), '') AGE,off_set,rn,max(rn) over(partition by id) max_rn,id idrrr
into #cte_results
FROM cte
/*
the following query is used to display the results in the screen dynamically
*/
declare #pre_offset int=0, #post_offset int =2
set #post_offset=( select top 1 max(max_rn)-max(rn)
from #cte_results
where off_Set
between #pre_offset and #post_offset
group by idrrr
order by idrrr desc
)+#post_offset
select id,name,age from #cte_results
where off_Set
between #pre_offset and #post_offset
Results were as follows

Query to select and combine uppermost and lowermost value from tables

We have the following tables on our SQL Server 2012.
Table A (data):
ID, Description
---------------
1 , Bla 1
2 , Bla 2
3 , Bla 3
Table P (data):
ID, ParentID, Name
------------------
1 , NULL , AAA
2 , 3 , CCC
3 , 1 , XXX
Table X (foreign keys A_ID to A.ID and P_ID to P.ID):
ID, A_ID, P_ID
--------------
1 , 1 , 1
2 , 1 , 2
3 , 2 , 1
4 , 2 , 2
5 , 2 , 3
6 , 3 , 1
Question:
We need a query something like:
SELECT ...
WHERE A_ID = 1
which should return this result:
ID, Name, Subname
-----------------
2 , AAA , CCC
Name needs to contain the upper most Name from Table P, i.e. the one that has no ParentID.
Subname needs to contain the bottom most Name from the Table P for which the ID still exists in Table X.
ID needs to contain the ID from Table X where P_ID is the ID of the bottom most child.
Another example:
SELECT ...
WHERE A_ID = 2
should return this result:
ID, Name, Subname
-----------------
4 , AAA , CCC
And
SELECT ...
WHERE A_ID = 3
should return this result:
ID, Name, Subname
-----------------
6 , AAA , NULL
We've tried various queries, but some work only for 'where A_ID = 1' and not for 'where A_ID = 2'. In order to select the lowest level child from P, we've looked at the 'How to select lowest level in hierarchy form table post' which probably comes in handy for the query we're looking for.
A single query would be nice, but we will accept a stored procedure as well.
Thanks in advance!
Information
The ID columns in all tables are primary keys
The ID columns in any given table can be changed to any other value in the sample data, while taking into account the primary and foreign key constraints. (E.g. changing P.ID '2' to '4' also results in the change of X.P_ID's '2' to '4'.) This is to show that ID's are not necessarily in order.
Values in the column P.Name can be any non-null value.
Table P can have multiple rows with ParentId set to null.
Sample Data
Taken from #NEER
DECLARE #A TABLE (ID INT, DESCRIPTION NVARCHAR(10))
INSERT INTO #A
VALUES
(1 , 'Bla 1'),
(2 , 'Bla 2'),
(3 , 'Bla 3')
DECLARE #P TABLE (ID INT, ParentID INT, Name NVARCHAR(10))
INSERT INTO #P
VALUES
(1 , NULL , 'AAA'),
(2 , 3 , 'CCC'),
(3 , 1 , 'XXX')
DECLARE #X TABLE (ID INT,A_ID INT,P_ID INT)
INSERT INTO #X
VALUES
(1 , 1 , 1),
(2 , 1 , 2),
(3 , 2 , 1),
(4 , 2 , 2),
(5 , 2 , 3),
(6 , 3 , 1)
Try with the below query. I think Table A is not required for getting the desired result.
SELECT TOP 1 First_VALUE(x.ID) OVER(ORDER BY x.ID desc) ID
,First_VALUE(Name) OVER(ORDER BY p.ID) Name
,CASE WHEN First_VALUE(Name) OVER(ORDER BY p.ID) = First_VALUE(Name) OVER(ORDER BY p.ID desc) THEN NULL
ELSE First_VALUE(Name) OVER(ORDER BY p.ID desc) END SubName
FROM [table P] p
JOIN [table X] x
ON p.ID=x.[P_ID]
WHERE x.[A_ID]=3
Try following query
DECLARE #TableA AS TABLE (ID INT,Des NVARCHAR(MAX));
Insert Into #TableA VALUES(1,'Bal 1'); Insert Into #TableA VALUES(2,'Bal 2'); Insert Into #TableA VALUES(3,'Bal 3');
DECLARE #TableP AS TABLE (ID INT,ParentID INT,Name NVARCHAR(MAX));
Insert Into #TableP VALUES(1,Null,'AAA'); Insert Into #TableP VALUES(2,1,'BBB'); Insert Into #TableP VALUES(3,2,'CCC');
DECLARE #TableX AS TABLE (ID INT,A_ID INT,P_ID INT);
Insert Into #TableX Values(1,1,1); Insert Into #TableX Values(2,1,2); Insert Into #TableX Values(3,2,1); Insert Into #TableX Values(4,2,3); Insert Into #TableX Values(5,3,1);
Select Top 1 X.ID,(Select top 1 Name from #TableP Where ParentID is null) Name,P.Name as SubName
from #TableX as X
Inner Join #TableP as P On P.ID=x.P_ID And P.ParentID IS Not Null
Where A_ID=1
Order by X.ID Desc
Select Top 1 X.ID,(Select top 1 Name from #TableP Where ParentID is null) Name,P.Name as SubName
from #TableX as X
Left Join #TableP as P On P.ID=x.P_ID And P.ParentID IS Not Null
Where A_ID=2
Order by X.ID Desc
Select Top 1 X.ID,(Select top 1 Name from #TableP Where ParentID is null) Name,P.Name as SubName
from #TableX as X
Left Join #TableP as P On P.ID=x.P_ID And P.ParentID IS Not Null
Where A_ID=3
Order by X.ID Desc
You can try the following
with report as (
select max(x.ID) as ID, min(x.P_ID) as MinP, max(x.P_ID) as MaxP
from X x
where x.A_ID = 1 -- <-- here you can change the value
)
select r.ID,
mn.Name as Name,
case when r.MinP = r.MaxP then null else mx.Name end as Subname
from report r
inner join P mn on mn.ID = r.MinP
inner join P mx on mx.ID = r.MaxP
Hope this will help you
Try it with a GROUP BY:
SELECT x.a_id, max(x.id) AS id, min(p.name) AS name,
CASE WHEN max(p.name) = min(p.name) THEN NULL
ELSE max(p.name) END AS subname
FROM p INNER JOIN x
ON p.id = x.p_id
GROUP BY x.a_id
HAVING x.a_id = 1
Still works with your updated sample data. Tested here: http://sqlfiddle.com/#!9/99597f/1
You can use Recursive CTE as the below:
DECLARE #A TABLE (ID INT, DESCRIPTION NVARCHAR(10))
INSERT INTO #A
VALUES
(1 , 'Bla 1'),
(2 , 'Bla 2'),
(3 , 'Bla 3')
DECLARE #P TABLE (ID INT, ParentID INT, Name NVARCHAR(10))
INSERT INTO #P
VALUES
(1 , NULL , 'AAA'),
(2 , 3 , 'CCC'),
(3 , 1 , 'XXX')
DECLARE #X TABLE (ID INT,A_ID INT,P_ID INT)
INSERT INTO #X
VALUES
(1 , 1 , 1),
(2 , 1 , 2),
(3 , 2 , 1),
(4 , 2 , 2),
(5 , 2 , 3),
(6 , 3 , 1)
DECLARE #A_ID INT = 2
;WITH Parents
AS
(
SELECT
P.ID, P.ParentID, P.Name
FROM #P P WHERE P.ParentID IS NULL
UNION ALL
SELECT
P.ID, Parent.ID, Parent.Name
FROM
#P P INNER JOIN
Parents Parent ON P.ParentID = Parent.ID
), Temp
AS
(
SELECT
X.ID,
Parent.Name Name,
IIF(P.ParentID IS NULL, NULL, P.Name) SubName
FROM
#A A INNER JOIN
#X X ON X.A_ID = A.ID INNER JOIN
#P P ON X.P_ID = P.ID LEFT JOIN
Parents Parent ON P.ParentID = Parent.ID OR (P.ParentID IS NULL AND P.ID = Parent.ID)
WHERE
A.ID = #A_ID
), MainTable
AS
(
SELECT
Temp.ID ,
Temp.Name ,
Temp.SubName,
COUNT(Temp.ID) OVER (PARTITION BY Temp.Name ORDER BY (SELECT NULL)) CountOfRowByParent
FROM
Temp
)
SELECT
MainTable.ID ,
MainTable.Name ,
MainTable.SubName
FROM
MainTable
WHERE
(
MainTable.CountOfRowByParent > 1 AND
MainTable.SubName IS NOT NULL
) OR
MainTable.CountOfRowByParent = 1
Result for 2:
ID Name SubName
4 AAA CCC
5 AAA XXX

Consolidate data from three rows (with nulls) into one

I have three tables:
Profile
-ProfileID
-FirstName
-LastName
ProfilePhoneNumber
-ProfileID
-PhoneNumberID
PhoneNumber
-PhoneNumberID
-PhoneNumberTypeID
-Number
ProfilePhoneNumber is a simple bridge table between Profile and PhoneNumber.
I want to query for specific phone number types and return a single row. I want to be able to accept null values because not all people will have all types of phone numbers.
Here is my current query:
SELECT
p.FirstName
, p.LastName
, bpn.Number as BusinessPhoneNumber
, mpn.Number as MobilePhoneNumber
FROM Profile p
LEFT JOIN ProfilePhoneNumber ppn ON p.ProfileID = ppn.ProfileID
LEFT JOIN PhoneNumber bpn ON ppn.PhoneNumberID = bpn.PhoneNumberID AND bpn.PhoneNumberTypeID = '1'
LEFT JOIN PhoneNumber mpn ON ppn.PhoneNumberID = mpn.PhoneNumberID AND mpn.PhoneNumberTypeID = '2'
WHERE p.ProfileID = '123'
This always works, but returns three rows because Profile 123 has three phone numbers, and so the query returns a row for each phone number.
If I change it to an INNER JOIN on PhoneNumber, I can get only one row back, but only in circumstances where the Profile being queried has all of the PhoneNumberTypeID types that I am querying for.
How do I return one row that is null tolerant?
I will update the query later, if you provide some data sample and expected output.
So far, will this help?
SELECT
p.FirstName
, p.LastName
, Max(bpn.Number) as BusinessPhoneNumber
, Max(mpn.Number) as MobilePhoneNumber
FROM Profile p
LEFT JOIN ProfilePhoneNumber ppn on p.ProfileID = ppn.ProfileID
LEFT JOIN PhoneNumber bpn on ppn.PhoneNumberID
= bpn.PhoneNumberID AND bpn.PhoneNumberTypeID = '1'
LEFT JOIN PhoneNumber mpn on ppn.PhoneNumberID
= mpn.PhoneNumberID AND mpn.PhoneNumberTypeID = '2'
WHERE p.ProfileID = '123'
group by p.FirstName, p.LastName;
If each profile has one phone per type then you can:
Use INNER JOIN queries to match profile with each phone type
Use LEFT JOIN query to match the profiles with the above
DDL:
CREATE TABLE profile (ProfileID INT NOT NULL, FirstName VARCHAR(100), LastName VARCHAR(100), PRIMARY KEY (ProfileID));
INSERT INTO profile (ProfileID, FirstName, LastName) VALUES (1, 'User', '#1'), (2, 'User', '#2'), (3, 'User', '#3');
CREATE TABLE phonenumber (PhoneNumberID INT NOT NULL, PhoneNumberTypeID INT, Number VARCHAR(100), PRIMARY KEY (PhoneNumberID));
INSERT INTO phonenumber (PhoneNumberID, PhoneNumberTypeID, Number) VALUES (1, 1, '0800-U1BUS'), (2, 1, '0800-U2BUS'), (3, 2, '0800-U2MOB'), (4, 1, '0800-U3BUS'), (5, 2, '0800-U3MOB'), (6, 3, '0800-U3ETC');
CREATE TABLE profilephonenumber (ProfileID INT NOT NULL, PhoneNumberID INT NOT NULL, PRIMARY KEY (ProfileID,PhoneNumberID));
INSERT INTO profilephonenumber (ProfileID, PhoneNumberID) VALUES (1, 1), (2, 2), (2, 3), (3, 4), (3, 5), (3, 6);
Query:
SELECT Profile.FirstName, Profile.LastName, BusPhone.Number AS BusPhoneNumber, MobPhone.Number AS MobPhoneNumber
FROM profile
LEFT JOIN (
SELECT ProfileID, Number
FROM profilephonenumber
INNER JOIN phonenumber ON profilephonenumber.PhoneNumberID = phonenumber.PhoneNumberID
WHERE PhoneNumberTypeID = 1
) AS BusPhone ON Profile.ProfileID = BusPhone.ProfileID
LEFT JOIN (
SELECT ProfileID, Number
FROM profilephonenumber
INNER JOIN phonenumber ON profilephonenumber.PhoneNumberID = phonenumber.PhoneNumberID
WHERE PhoneNumberTypeID = 2
) AS MobPhone ON Profile.ProfileID = MobPhone.ProfileID
Output:
+-----------+----------+----------------+----------------+
| FirstName | LastName | BusPhoneNumber | MobPhoneNumber |
+-----------+----------+----------------+----------------+
| User | #1 | 0800-U1BUS | NULL |
| User | #2 | 0800-U2BUS | 0800-U2MOB |
| User | #3 | 0800-U3BUS | 0800-U3MOB |
+-----------+----------+----------------+----------------+
SQL Fiddle
-- Sample data.
declare #Profile as Table ( ProfileId Int Identity, FirstName VarChar(10), LastName VarChar(10) );
insert into #Profile ( FirstName, LastName ) values
( 'Alice', 'Aardvark' ), ( 'Bob', 'Bear' ), ( 'Cindy', 'Cat' );
declare #PhoneNumber as Table ( PhoneNumberId Int Identity, PhoneNumberTypeId Int, Number VarChar(10) );
insert into #PhoneNumber ( PhoneNumberTypeId, Number ) values
( 1, '1111111111' ), ( 2, '1112221111' ), ( 1, '1113331111' ), ( 2, '2222222222' );
declare #ProfilePhoneNumber as Table ( ProfileId Int, PhoneNumberId Int );
insert into #ProfilePhoneNumber ( ProfileId, PhoneNumberId ) values
( 1, 1 ), ( 1, 2 ),
( 2, 3 ),
( 3, 4 );
-- Dump the sample data.
select *
from #Profile as P left outer join
#ProfilePhoneNumber as PPN on PPN.ProfileId = P.ProfileId left outer join
#PhoneNumber as PN on PN.PhoneNumberId = PPN.PhoneNumberId;
-- Do the deed.
with ExtendedPhoneNumbers as (
select ProfileId, PhoneNumberTypeId, Number
from #ProfilePhoneNumber as PPN inner join
#PhoneNumber as PN on PN.PhoneNumberId = PPN.PhoneNumberId )
select P.FirstName, P.LastName,
EPNB.Number as BusinessPhoneNumber,
EPNM.Number as MobilePhoneNumber
from #Profile as P left outer join
ExtendedPhoneNumbers as EPNB on P.ProfileID = EPNB.ProfileID and EPNB.PhoneNumberTypeId = 1 left outer join
ExtendedPhoneNumbers as EPNM on P.ProfileID = EPNM.ProfileID and EPNM.PhoneNumberTypeId = 2
where P.ProfileId = 2; -- Comment out the WHERE clause to see all profiles.

SQL Server, Merge two records in one record

We have these tables
CREATE TABLE tbl01
(
[id] int NOT NULL PRIMARY KEY,
[name] nvarchar(50) NOT NULL
)
CREATE TABLE tbl02
(
[subId] int NOT NULL PRIMARY KEY ,
[id] int NOT NULL REFERENCES tbl01(id),
[val] nvarchar(50) NULL,
[code] int NULL
)
If we run this query:
SELECT
tbl01.id, tbl01.name, tbl02.val, tbl02.code
FROM
tbl01
INNER JOIN
tbl02 ON tbl01.id = tbl02.id
we get these results:
-------------------------------
id | name | val | code
-------------------------------
1 | one | FirstVal | 1
1 | one | SecondVal | 2
2 | two | YourVal | 1
2 | two | OurVal | 2
3 | three | NotVal | 1
3 | three | ThisVal | 2
-------------------------------
You can see that each two rows are related to same "id"
The question is: we need for each id to retrieve one record with all val, each val will return in column according to the value of column code
if(code = 1) then val as val-1
else if (code = 2) then val as val-2
Like this:
-------------------------------
id | name | val-1 | val-2
-------------------------------
1 | one | FirstVal | SecondVal
2 | two | YourVal | OurVal
3 | three | NotVal | ThisVal
-------------------------------
Any advice?
Use can use MAX and Group By to achieve this
SELECT id,
name,
MAX([val1]) [val-1],
MAX([val2]) [val-2]
FROM ( SELECT tbl01.id, tbl01.name,
CASE code
WHEN 1 THEN tbl02.val
ELSE ''
END [val1],
CASE code
WHEN 2 THEN tbl02.val
ELSE ''
END [val2]
FROM tbl01
INNER JOIN tbl02 ON tbl01.id = tbl02.id
) Tbl
GROUP BY id, name
Is it the PIVOT operator (http://technet.microsoft.com/en-us/library/ms177410(v=sql.105).aspx) that you are looking for?
You've already got a few answers, but heres one using PIVOT as an alternative. The good thing is this approach is easy to scale if there are additional columns required later
-- SETUP TABLES
DECLARE #t1 TABLE (
[id] int NOT NULL PRIMARY KEY,
[name] nvarchar(50) NOT NULL
)
DECLARE #t2 TABLE(
[subId] int NOT NULL PRIMARY KEY ,
[id] int NOT NULL,
[val] nvarchar(50) NULL,
[code] int NULL
)
-- SAMPLE DATA
INSERT #t1 ( id, name )
VALUES ( 1, 'one'), (2, 'two'), (3, 'three')
INSERT #t2
( subId, id, val, code )
VALUES ( 1,1,'FirstVal', 1), ( 2,1,'SecondVal', 2)
,( 3,2,'YourVal', 1), ( 4,2,'OurVal', 2)
,( 5,3,'NotVal', 1), ( 6,3,'ThisVal', 2)
-- SELECT (using PIVOT)
SELECT id, name, [1] AS 'val-1', [2] AS 'val-2'
FROM
(
SELECT t2.id, t1.name, t2.val, t2.code
FROM #t1 AS t1 JOIN #t2 AS t2 ON t2.id = t1.id
) AS src
PIVOT
(
MIN(val)
FOR code IN ([1], [2])
) AS pvt
results:
id name val-1 val-2
---------------------------------
1 one FirstVal SecondVal
2 two YourVal OurVal
3 three NotVal ThisVal
If there are always only two values, you could join them or even easier, group them:
SELECT tbl01.id as id, Min(tbl01.name) as name, MIN(tbl02.val) as val-1, MAX(tbl02.val) as val-2
FROM tbl01
INNER JOIN tbl02 ON tbl01.id = tbl02.id
GROUP BY tbl02.id
note: this query will always put the lowest value in the first column and highest in the second, if this is not wanted: use the join query:
Join query
If you always want code 1 in the first column and code 2 in the second:
SELECT tbl01.id as id, tbl01.name as name, tbl02.val as val-1, tbl03.val as val-2
FROM tbl01
INNER JOIN tbl02 ON tbl01.id = tbl02.id
ON tbl02.code = 1
INNER JOIN tbl03 ON tbl01.id = tbl03.id
ON tbl03.code = 2
Variable amount of columns
You cannot get an variable amount of columns, only when you do this by building your query in code or t-sql stored procedures.
My advice:
If its always to values: join them in query, if not, let your server-side code transform the data. (or even better, find a way which makes it not nessecery to transform data)
Try this - it uses a pivot function but it also creates creates the dynamic columns dependent on code
DECLARE #ColumnString varchar(200)
DECLARE #sql varchar(1000)
CREATE TABLE #ColumnValue
(
Value varchar(500)
)
INSERT INTO #ColumnValue (Value)
SELECT DISTINCT '[' + 'value' + Convert(Varchar(20),ROW_NUMBER() Over(Partition by id Order by id )) + ']'
FROM Test
SELECT #ColumnString = COALESCE(#ColumnString + ',', '') + Value
FROM #ColumnValue
Drop table #ColumnValue
SET #sql =
'
SELECT *
FROM
(
SELECT
id,name,val,''value'' + Convert(Varchar(20),ROW_NUMBER() Over(Partition by id Order by id ))as [values]
FROM Test
) AS P
PIVOT
(
MAX(val) FOR [values] IN ('+#ColumnString+')
) AS pv
'
--print #sql
EXEC (#sql)