Insert Missing Values Into Table Using SQL - sql

Objective: I need to fully populate a table with a matrix of values for each column by [PropertyId] grouping. Several [PropertyId] have all the necessary values for each column (Table 1), however, many are missing some values (Table 2). Furthermore, not every [PropertyId] needs these values as they have completely different regional values. Therefore, I need to identify which [PropertyId] both need the values populated and don't have all the necessary values.
Examples:
Table 1. Each identified [PropertyId] grouping should have 23 distinct records for these four columns [ReportingVolumeSettingId],[SpeciesGroupInventoryID],[CropCategoryID],[SortOrder].
Table 2. Here is an example of a PropertyID that is missing a value combination as it only has 22 records:
Both of these example results were queried from the same table [ReportingVolume]. I have not been successful in even identifying which record combination per [PropertyID] are missing. I would like to identify each missing record combination and then insert that record combination into the [ReportingVolume] table.
Problem to Solve -- The SQL Code below is my attempt to 1. Identify the correct List of Values; 2. Identify which properties should have matching values; 3. Identify which properties are missing values; 4. Identify the missing values per property.
;with CORRECT_LIST as
(
select
SpeciesGroupInventoryName, SpeciesGroupInventoryId, CropCategoryName,CropCategoryID, UnitOfMeasure, SortOrder
--*
from [GIS].[RST].[vPropertyDefaultTimberProductAndUnitOfMeasure]
where PropertyId in (1)
)
,
property_list as
(
select distinct rvs.propertyid as Volume_Property, pd.PropertyName, pd.PropertyId from RMS.GIS.ReportingVolumeSetting rvs
right outer JOIN RMS.GIS.PropertyDetail AS pd ON rvs.PropertyId = pd.PropertyId
left outer JOIN RMS.GIS.SpeciesGroupInventory AS sgi ON rvs.SpeciesGroupInventoryId = sgi.SpeciesGroupInventoryId
where sgi.SpeciesGroupInventoryId in (1,2,3)
or pd.PropertyId = 171
)
, Partial_LISTS as
(
select Count(distinct ReportingVolumeSettingId) as CNT_REPORT, pd.PropertyName, pd.PropertyId
from [GIS].[ReportingVolumeSetting] rvs
right outer JOIN property_list AS pd ON rvs.PropertyId = pd.PropertyId
group by pd.propertyId, pd.PropertyName
)
, Add_Props as
(
select propertyName, propertyId, SUM(CNT_REPORT) as CNT_RECORDS from Partial_LISTS
where CNT_REPORT < 23
group by propertyName, propertyId
)
, RVS_RECORDS_PROPS as
(
select addProps.PropertyName, rvs.* from [GIS].[ReportingVolumeSetting] rvs
join Add_Props addProps on addprops.PropertyId = rvs.PropertyID
where rvs.PropertyId in (select PropertyId from Add_Props)
)
select rp.PropertyName, cl.*, rp.SpeciesGroupInventoryId from correct_list cl
left outer join RVS_Records_Props rp
on rp.SpeciesGroupInventoryId = cl.SpeciesGroupInventoryId
and rp.CropCategoryId = cl.CropCategoryID
and rp.SortOrder = cl.SortOrder
Order by rp.PropertyName
How can I modify the code or create a new code block identifies the missing values and inserts them into the table per PropertyId?
I am using SQL SMSS v15.
Thanks so much.

This should identify missing entries. You could simply add an INSERT INTO command on top of this. Keep in mind as the ReportingVolumeSettingId is unique and unknown it's not covered here.
SELECT * FROM (SELECT DISTINCT PropertyId FROM ReportingVolume) rv
CROSS APPLY
(
SELECT DISTINCT SpeciesGroupInventoryId
, CropCategoryId
, SortOrder
FROM ReportingVolume
) x
EXCEPT
SELECT PropertyId, SpeciesGroupInventoryId, CropCategoryId, SortOrder FROM ReportingVolume

I don't have access to your data, so I cannot provide an example specific to your environment, but I can provide you a simple example using SQL Server's EXCEPT operator.
Run the following example in SSMS:
-- Create a list of required values.
DECLARE #Required TABLE ( required_id INT, required_val VARCHAR(50) );
INSERT INTO #Required ( required_id, required_val ) VALUES
( 1, 'Required value 1.' ), ( 2, 'Required value 2.' ), ( 3, 'Required value 3.' ), ( 4, 'Required value 4.' ), ( 5, 'Required value 5.' );
-- Create some sample data to compare against.
DECLARE #Data TABLE ( PropertyId INT, RequiredId INT );
INSERT INTO #Data ( PropertyId, RequiredId ) VALUES
( 1, 1 ), ( 1, 2 ), ( 1, 3 ), ( 2, 1 ), ( 2, 2 ), ( 2, 4 ), ( 2, 5 );
-- Set a property id value to query.
DECLARE #PropertyId INT = 1;
-- Preview #Data's rows for the specified #PropertyId.
SELECT * FROM #Data WHERE PropertyId = #PropertyId ORDER BY PropertyId, RequiredId;
At this point, I've created a list of required values (required_id 1 through 5) and some dummy data to check them against. This initial SELECT shows the current resultset for the specified #PropertyID:
+------------+------------+
| PropertyId | RequiredId |
+------------+------------+
| 1 | 1 |
| 1 | 2 |
| 1 | 3 |
+------------+------------+
You can see that required_id values 4 and 5 are missing for the current property. Next, we can compare #Required against #Data and INSERT any missing required values using the EXCEPT operator and then return the corrected resultset.
-- Insert any missing required values for #PropertyId.
INSERT INTO #Data ( PropertyId, RequiredId )
SELECT #PropertyId, required_id FROM #Required
EXCEPT
SELECT PropertyId, RequiredId FROM #Data WHERE PropertyId = #PropertyId;
-- Preview #Data's revised rows for #PropertyId.
SELECT * FROM #Data WHERE PropertyId = #PropertyId ORDER BY PropertyId, RequiredId;
The updated resultset now looks like the following:
+------------+------------+
| PropertyId | RequiredId |
+------------+------------+
| 1 | 1 |
| 1 | 2 |
| 1 | 3 |
| 1 | 4 |
| 1 | 5 |
+------------+------------+
You can run this again against #PropertyId = 2 to see a different scenario.
Note the order to using EXCEPT. The required rows comes first, followed by the EXCEPT operator, and then the current rows to be validated. This is important. EXCEPT is saying show me rows from #Required that are not in #Data--which allows for the inserting of any missing required values into #Data.
I know this example doesn't represent your existing data with the 23 rows requirement, but hopefully, it will get you moving with a solution for your needs. You can read more about the EXCEPT operator here.
Here is the complete SSMS example:
-- Create a list of required values.
DECLARE #Required TABLE ( required_id INT, required_val VARCHAR(50) );
INSERT INTO #Required ( required_id, required_val ) VALUES
( 1, 'Required value 1.' ), ( 2, 'Required value 2.' ), ( 3, 'Required value 3.' ), ( 4, 'Required value 4.' ), ( 5, 'Required value 5.' );
-- Create some sample data to compare against.
DECLARE #Data TABLE ( PropertyId INT, RequiredId INT );
INSERT INTO #Data ( PropertyId, RequiredId ) VALUES
( 1, 1 ), ( 1, 2 ), ( 1, 3 ), ( 2, 1 ), ( 2, 2 ), ( 2, 4 ), ( 2, 5 );
-- Set a property id value to query.
DECLARE #PropertyId INT = 1;
-- Preview #Data's rows for the specified #PropertyId.
SELECT * FROM #Data WHERE PropertyId = #PropertyId ORDER BY PropertyId, RequiredId;
-- Insert any missing required values for #PropertyId.
INSERT INTO #Data ( PropertyId, RequiredId )
SELECT #PropertyId, required_id FROM #Required
EXCEPT
SELECT PropertyId, RequiredId FROM #Data WHERE PropertyId = #PropertyId;
-- Preview #Data's revised rows for #PropertyId.
SELECT * FROM #Data WHERE PropertyId = #PropertyId ORDER BY PropertyId, RequiredId;

Related

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

Common Table Expression to traverse down hierarchy

The Structure
I have 2 tables that link to each other. One is a set of values and a nullable foreign key that points to the Id of the other table, which contains 2 foreign keys back to the other table.
HierarchicalTable
Id LeftId RightId SomeValue
1 1 2 some value
2 3 4 top level in tree
3 5 6 incorrect hierarchy 1
4 7 8 incorrect result top level
IntermediateTable
Id SomeValue HierarchicalTableId
1 some value NULL
2 value NULL
3 NULL 1
4 value NULL
5 incorrect result 1 NULL
6 incorrect result 3 NULL
7 incorrect result 3 NULL
8 NULL 3
Each table points down the hierarchy. Here is this structure graphed out for the Hierarchical Table records 1 & 2 and their IntermediateTable values:
(H : HierarchicalTable, I : IntermediateTable)
H-2
/ \
I-3 I-4
/
H-1
/ \
I-1 I-2
The Problem
I need to be able to send in an Id for a given HierarchicalTable and get all the HierarchicalTable records below it. So, for the structure above, if I pass 1 into a query, I should just get H-1 (and from that, I can load the related IntermediateTable values). If I pass 2, I should get H-2 and H-1 (and, again, use those to load the relevant IntermediateTable values).
The Attempts
I've tried using a CTE, but there are a few main things that are different from the examples I've seen:
In my structure, the objects point down to their children, instead of up to their parent
I have the Id of the top object, not the Id of the bottom object.
My hierarchy is split across 2 tables. This shouldn't be a big issue once I understand the algorithm to find the results I need, but this could be causing additional confusion for me.
If I run this query:
declare #TargetId bigint = 2
;
with test as (
select h.*
from dbo.hierarchicaltable h
inner join dbo.intermediatetable i
on (h.leftid = i.id or h.rightid = i.id)
union all
select h.*
from dbo.hierarchicaltable h
where h.id = #TargetId
)
select distinct *
from test
I get all 4 records in the HierarchicalTable, instead of just records 1 & 2. I'm not sure if what I want is possible to do with a CTE.
Try this:
I'm build entire tree with both tables, then filter (only hierarchicaltable records).
DECLARE #HierarchicalTable TABLE(
Id INT,
LeftId INT,
RightId INT,
SomeValue VARCHAR(MAX)
)
INSERT INTO #HierarchicalTable
VALUES
(1, 1, 2, 'some value '),
(2, 3, 4, 'top level in tree '),
(3, 5, 6, 'incorrect hierarchy 1 '),
(4, 7, 8, 'incorrect result top level')
DECLARE #IntermediateTable TABLE(
Id INT,
SomeValue VARCHAR(MAX),
HierarchicalTableId INT
)
INSERT INTO #IntermediateTable
VALUES
(1, 'some value' ,NULL ),
(2, 'value ' ,NULL ),
(3, NULL ,1 ),
(4, 'value ' ,NULL ),
(5, 'incorrect result 1' ,NULL ),
(6, 'incorrect result 3' ,NULL ),
(7, 'incorrect result 3' ,NULL ),
(8, NULL ,3 )
DECLARE #TargetId INT = 2;
WITH CTE AS (
SELECT Id AS ResultId, LeftId, RightId, NULL AS HierarchicalTableId
FROM #HierarchicalTable
WHERE Id = #TargetId
UNION ALL
SELECT C.Id AS ResultId, C.LeftId, C.RightId, NULL AS HierarchicalTableId
FROM #HierarchicalTable C
INNER JOIN CTE P ON P.HierarchicalTableId = C.Id
UNION ALL
SELECT NULL AS ResultId, NULL AS LeftId, NULL AS RightId, C.HierarchicalTableId
FROM #IntermediateTable C
INNER JOIN CTE P ON P.LeftId = C.Id OR P.RightId = C.Id
)
SELECT *
FROM CTE
WHERE ResultId IS NOT NULL

SQL: Upsert and get the old and the new values

I have the following table Items:
Id MemberId MemberGuid ExpiryYear Hash
---------------------------------------------------------------------------
1 1 Guid1 2017 Hash1
2 1 Guid2 2018 Hash2
3 2 Guid3 2020 Hash3
4 2 Guid4 2017 Hash1
I need to copy the items from a member to another (not just to update MemberId, to insert a new record). The rule is: if I want to migrate all the items from a member to another, I will have to check that that item does not exists in the new member.
For example, if I want to move the items from member 1 to member 2, I will move only item with id 2, because I already have an item at member 2 with the same hash and with the same expiry year (this are the columns that I need to check before inserting the new items).
How to write a query that migrates only the non-existing items from a member to another and get the old id and the new id of the records? Somehow with an upsert?
You can as the below:
-- MOCK DATA
DECLARE #Tbl TABLE
(
Id INT IDENTITY NOT NULL PRIMARY KEY,
MemberId INT,
MemberGuid CHAR(5),
ExpiryYear CHAR(4),
Hash CHAR(5)
)
INSERT INTO #Tbl
VALUES
(1, 'Guid1', '2017', 'Hash1'),
(1, 'Guid2', '2018', 'Hash1'),
(2, 'Guid3', '2020', 'Hash3'),
(2, 'Guid4', '2017', 'Hash1')
-- MOCK DATA
-- Parameters
DECLARE #FromParam INT = 1
DECLARE #ToParam INT = 2
DECLARE #TmpTable TABLE (NewDataId INT, OldDataId INT)
MERGE #Tbl AS T
USING
(
SELECT * FROM #Tbl
WHERE MemberId = #FromParam
) AS F
ON T.Hash = F.Hash AND
T.ExpiryYear = F.ExpiryYear AND
T.MemberId = #ToParam
WHEN NOT MATCHED THEN
INSERT ( MemberId, MemberGuid, ExpiryYear, Hash)
VALUES ( #ToParam, F.MemberGuid, F.ExpiryYear, F.Hash)
OUTPUT inserted.Id, F.Id INTO #TmpTable;
SELECT * FROM #TmpTable
Step 1:
Get in cursor all the data of member 1
Step 2:
While moving through cursor.
Begin
select hash, expirydate from items where memberid=2 and hash=member1.hash and expirydate=member1.expirydate
Step 3
If above brings any result, do not insert.
else insert.
Hope this helps
Note: this is not actual code. I am providing you just steps based on which you can write sql.
Actually you just need an insert. When ExpiryYear and Hash matched you don't wanna do anything. You just wanna insert from source to target where those columns doesn't match. You can do that with Merge or Insert.
CREATE TABLE YourTable
(
Oldid INT,
OldMemberId INT,
Id INT,
MemberId INT,
MemberGuid CHAR(5),
ExpiryYear CHAR(4),
Hash CHAR(5)
)
INSERT INTO YourTable VALUES
(null, null, 1, 1, 'Guid1', '2017', 'Hash1'),
(null, null, 2, 1, 'Guid2', '2018', 'Hash2'),
(null, null, 3, 2, 'Guid3', '2020', 'Hash3'),
(null, null, 4, 2, 'Guid4', '2017', 'Hash1')
DECLARE #SourceMemberID AS INT = 1
DECLARE #TargetMemberID AS INT = 2
MERGE [YourTable] AS t
USING
(
SELECT * FROM [YourTable]
WHERE MemberId = #SourceMemberID
) AS s
ON t.Hash = s.Hash AND t.ExpiryYear = s.ExpiryYear AND t.MemberId = #TargetMemberID
WHEN NOT MATCHED THEN
INSERT(Oldid, OldMemberId, Id, MemberId, MemberGuid, ExpiryYear, Hash) VALUES (s.Id, s.MemberId, (SELECT MAX(Id) + 1 FROM [YourTable]), #TargetMemberID, s.MemberGuid, s.ExpiryYear, s.Hash);
SELECT * FROM YourTable
DROP TABLE YourTable
/* Output:
Oldid OldMemberId Id MemberId MemberGuid ExpiryYear Hash
-----------------------------------------------------------------
NULL NULL 1 1 Guid1 2017 Hash1
NULL NULL 2 1 Guid2 2018 Hash2
NULL NULL 3 2 Guid3 2020 Hash3
NULL NULL 4 2 Guid4 2017 Hash1
2 1 5 2 Guid2 2018 Hash2
If you just want to select then do as following
SELECT null AS OldID, null AS OldMemberID, Id, MemberId, MemberGuid, ExpiryYear, Hash FROM YourTable
UNION ALL
SELECT A.Id AS OldID, A.MemberId AS OldMemberID, (SELECT MAX(Id) + 1 FROM YourTable) AS Id, #TargetMemberID AS MemberId, A.MemberGuid, A.ExpiryYear, A.Hash
FROM YourTable A
LEFT JOIN
(
SELECT * FROM YourTable WHERE MemberId = #TargetMemberID
) B ON A.ExpiryYear = B.ExpiryYear AND A.Hash = B.Hash
WHERE A.MemberId = #SourceMemberID AND B.Id IS NULL

How to return only numbers from query where column is nvarchar

I have a simple query that is returning records where "column2" > 0
Here is the data in the database
Column1 Column2
1 123456789
2 123456781
3 13-151-1513
4 alsdjf
5
6 000000000
Her is the query
select column1, replace(a.Payroll_id,'-','')
from table1
where isnumeric(column2) = 1
I'd like to return the following:
Column1 Column2
1 123456789
2 123456781
3 131511513
This mean, I won't select any records when the column is blank (or null), will not return a row if it's not an integer, and will drop out the '-', and would not show row 6 since it's all 0.
How can I do this?
I think you can use something like this :
USE tempdb
GO
CREATE TABLE #Temp
(
ID INT IDENTITY
,VALUE VARCHAR(30)
)
INSERT INTO #Temp (VALUE) VALUES ('1213213'), ('1213213'), ('121-32-13'), ('ASDFASF2123')
GO
WITH CteData
AS
(
SELECT REPLACE(VALUE,'-','') as Valor FROM #Temp
)
SELECT * FROM CteData WHERE (ISNUMERIC(Valor) = 1 AND valor not like '%[0-0]%')
DROP TABLE #Temp
then you can apply validations for empty, NULL,0 etc
If you are using SQL2012 or above you can also use TRY_PARSE that is more selective in its parsing. This function will return NULL if a record can't be converted. You could use it like this:
CREATE TABLE #temp
(
ID INT IDENTITY ,
VALUE VARCHAR(30)
)
INSERT INTO #temp
( VALUE )
VALUES ( '1213213' ),
( '1213213' ),
( '121-32-13' ),
( 'ASDFASF2123' ),
( '0000000' )
SELECT ParsedValue
FROM #temp
CROSS APPLY ( SELECT TRY_PARSE(
Value AS INT ) AS ParsedValue
) details
WHERE ParsedValue IS NOT NULL
AND ParsedValue>0

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)