How to output using recursive (parents -> children -> grandchildren) - sql-server

CREATE TABLE PERSON
(
persID INT IDENTITY(1,1) PRIMARY KEY,
persFName VARCHAR(30) NOT NULL,
persLName VARCHAR(30) NOT NULL,
persDOB DATE,
motherID INT FOREIGN KEY REFERENCES person(persID),
fatherID INT FOREIGN KEY REFERENCES person(persID),
persDOD DATE,
persGender CHAR(1),
)
CREATE TABLE COUPLE
(
coupleID INT IDENTITY(1,1) PRIMARY KEY,
alphaSpouse INT NOT NULL FOREIGN KEY REFERENCES person(persID),
omegaSpouse INT NOT NULL FOREIGN KEY REFERENCES person(persID),
coupleStart DATE NOT NULL,
coupleEnd DATE,
)
INSERT INTO person (persFName, persLName, persDOB, motherID, fatherID, persDOD, persGender) VALUES
('Jim', 'Smith', '1920-05-15', NULL, NULL, '2001-04-21','M'),
('Agnes', 'Smith', '1922-09-21', NULL, NULL, '2003-06-01','F'),
('Linda', 'Howard', '1949-03-20', 2, 1, NULL,'F'),
('Helen', 'Bradley', '1942-11-28', 2, 1, NULL,'F'),
('Jim', 'Smith', '1953-02-10', 2, 1, NULL,'M'),
('Stephen', 'Smith', '1957-01-13', 2, 1, '2005-05-25','M'),
('Daniel', 'Smith', '1961-11-28', 2, 1, NULL,'M'),
('Michelle', 'Smith', '1963-07-02', NULL, NULL, NULL,'F'),
('Anne', 'Smith', '1958-04-22', NULL, NULL, NULL,'F'),
('Josh', 'Smith', '1990-01-28', 8, 7, NULL,'M'),
('Stephanie', 'Smith', '1992-05-18', 8, 7, NULL,'F'),
('Rachel', 'Smith', '1996-11-05', 8, 7, NULL,'F'),
('Reg', 'Howard', '1949-07-07', NULL, NULL, NULL,'M'),
('Richard', 'Howard', '1972-12-26', 3, 13, NULL,'M'),
('Phillip', 'Howard', '1975-06-01', 3, 13, NULL,'M'),
('Robert', 'Bradley', '1939-03-07', NULL, NULL, NULL,'M'),
('Matthew', 'Bradley', '1964-10-26', 4, 16, '2001-04-27','M'),
('Yvonne', 'Bradley', '1966-11-28', 4, 16, NULL,'F'),
('James', 'Bradley', '1971-01-26', 4, 16, NULL,'M'),
('Anna', 'Reuper', '1918-01-27', NULL, NULL, NULL,'F'),
('Ludwig', 'Reuper', '1923-07-01', NULL, NULL, '2001-07-01','M'),
('Heinz', 'Reuper', '1965-01-28', 20, 21, NULL,'M'),
('Veronica', 'Reuper', '1959-04-01', 20, 21, NULL,'F'),
('Marco', 'Johnson', '1958-09-04', NULL, NULL, NULL,'M'),
('Francesca', 'Reuper', '1990-11-01', 23, 24, NULL,'F'),
('Lou', 'Jung', '1940-02-03', NULL, NULL, NULL,'M'),
('Tammi', 'Sinclair', '1971-02-03', NULL, NULL, NULL,'F'),
('Lori', 'Navarro', '1973-12-15', NULL, NULL, NULL,'F'),
('Kattie', 'Paine', '1980-10-31', NULL, NULL, NULL,'F');
INSERT INTO couple (alphaSpouse,omegaSpouse, coupleStart, coupleEnd) VALUES
(1,2,'1940-05-22', NULL),
(3,13,'1969-07-30', '1973-10-01'),
(3,26,'1980-01-10', '1982-12-01'),
(4,16,'1963-04-05', NULL),
(6,9,'1979-06-21', NULL),
(7,8,'1987-09-14', NULL),
(20,21,'1936-10-26', NULL),
(23,24,'1988-05-01', '1997-07-01'),
(19,27,'1992-10-31', '1993-01-31'),
(19,28,'1995-08-18', '1997-04-27'),
(19,29,'2015-09-19', NULL);
I'm trying to list all the people who have grandchildren and how many grandchildren they each have. I'd like an ouput like this;
I attempted to do it below, but I got stuck trying to COUNT grandkids outside the WHERE subquery.. was I even on the right track?
SELECT grandp.persFName + ' ' + grandp.persLName, COUNT(grandkids.persID)
FROM person grandp
JOIN person child ON grandp.persID = child.motherID OR grandp.persID = child.fatherID
WHERE child.persID IN (SELECT grandkids.motherID
FROM person grandkids
union
SELECT grandkids.fatherID
FROM person grandkids)
GROUP BY grandp.persFName + ' ' + grandp.persLName

When you have hierarchical data like yours, it's best approached with common table expressions. This will allow you to build the hierarchical data into a form that you can later use - in your example, to count grandchildren. You can take advantage of building data by levels or distance from the root.
with temp (persid, ancestorid, level) as (
select persid, persid as ancestorid, 0 as level
from person
where motherid is null and fatherid is null
union all
select person.persid, temp.ancestorid, temp.level + 1 as level
from person
inner join temp on temp.persid = person.motherid or temp.persid = person.fatherid
)
select temp.ancestorid as persid,
person.persFName + ' ' + person.persLName as name,
count(*)
from temp
inner join person
on temp.ancestorid = person.persid
where level = 2
group by temp.ancestorid, person.persFName, person.persLName
The query builds a CTE of each person, it's ancestor and the level (0 = grandparents, 2 = grandchildren). You can then easily count grandchildren for each ancestor.
SQL Fiddle: http://sqlfiddle.com/#!3/99c97/2
(Note: I left the couple table out as it's not needed here).

Try this I just used common table expressions recursively to get the result
;WITH Allpersons AS
(
SELECT persID AS Id
,persFName+' '+persLName AS Name
,fatherID
,motherID
FROM PERSON
)
SELECT
GrandFatherCTE.Name
,COUNT(GrandFatherCTE.Id ) AS CountOfChild
FROM Allpersons AllPersonsCTE
INNER JOIN
Allpersons ParentsCte
ON
AllPersonsCTE.fatherID = ParentsCte.Id
OR
AllPersonsCTE.motherID = ParentsCte.Id
INNER JOIN
Allpersons GrandFatherCTE
ON
ParentsCte.fatherID = GrandFatherCTE.Id
OR
ParentsCte.motherID = GrandFatherCTE.Id
INNER JOIN
COUPLE
ON
GrandFatherCTE.Id = COUPLE.alphaSpouse
OR
GrandFatherCTE.Id = COUPLE.omegaSpouse
GROUP BY
GrandFatherCTE.Name
,COUPLE.coupleID
ORDER BY
CountOfChild ASC
,COUPLE.coupleID ASC

Related

SQL Creating table Employee Department Grade

create database staff_management;
use staff_management;
create table Employee
(
eID int(100) NOT NULL IDENTITY(1,1) PRIMARY KEY,
eName varchar(255) NOT NULL,
Job text NOT NULL,
Salary int(100) NOT NULL,
Comm int(100),
hDate date NOT NULL,
dID int(10) NOT NULL,
constraint emp_pk primary key (eID)
);
alter table Employee IDENTITY(1,1) PRIMARY KEY=1001;
alter table Employee
add column Mgr int(100) after eName;
insert into Employee(eName,Mgr, Job, Salary, Comm, hDate, dID)
values( "ken Adams", 1004, "Salesman", 70000, 20000, "2008-04-12", 1),
("Ru Jones", 1004, "Salesman", 65000, 15000, "2010-01-18", 1),
( "Dhal Sim", 1006, "Accountant", 88000, NULL, "2001-03-07", 2),
( "Ellen Honda", 1006, "Manager", 118000, NULL, "2001-03-17", 1),
( "Mike Bal", 1006, "Receptionist", 68000, NULL, "2006-06-21", 3),
( "Martin Bison",NULL, "CEO", 210000, NULL, "2010-07-12", 3),
( "Shen Li", 1004, "Salesman", 86000, 18000, "2014-09-18", 1),
( "Zang Ross", 1004, "Salesman", 65000, 10000, "2017-02-02", 1),
( "Sagar Kahn", 1004, "Salesman", 70000, 15000, "2016-03-01", 1);
alter table Employee
add constraint emp_mgr_fk foreign key (Mgr) references Employee(eID) on update cascade on delete set NULL;
create table Department
(
dID int(10) NOT NULL unique IDENTITY(1,1) PRIMARY KEY,
dName varchar(255) not null,
Address text,
phone text,
constraint d_pk primary key (dID)
);
alter table Employee
add constraint emp_d_fk
foreign key (dID) references Department(dID);
create table Grade
(
gID char(10) not null unique,
MinSal int(100),
MaxSal int(100),
Leavee int(10),
constraint g_pk primary key (gID)
);
INSERT INTO Grade (gID, MinSal, MaxSal, Leavee)
VALUES ('A', NULL, 60000, 20),
('B', 60000, 80000, 20),
('C', 80000, 100000, 20),
('D', 100000, 120000, 25),
('E', 120000, NULL, 30);
select * from Grade;
insert into Department (dName, Address, phone)
values("Sales", "Sydney", "0425 198 053"),
("Accounts", "Melbourne", "0429 198 955"),
("Admin", "Melbourne", "0428 198 758"),
("Marketing", "Sydney", "0427 198 757");
select * from Department;
I'm issue with my code
Msg 156, Level 15, State 1, Line 18
Incorrect syntax near the keyword 'IDENTITY'.
Msg 156, Level 15, State 1, Line 21
Incorrect syntax near the keyword 'column'.
The syntax for SQL Server is NOT auto_increment - you need to use an INT IDENTITY column instead. And also: in T-SQL an INT is an INT is an INT - no "precision" can be defined):
Instead of this:
create table Employee
(
eID int(100) NOT NULL auto_increment,
use this:
CREATE TABLE dbo.Employee
(
eID INT NOT NULL IDENTITY(1,1),
This question is posted under SQL-Server which is Microsoft not MySQL but your syntax appears to be for MySQL.
If you are running MSSQL the code below will work but you need to look at your foreign keys as you are trying to make a foreign key for a column on the same table. Normally when a key is introduced it is in relation to another table. I.E.the FK on Grade references the PK on Employee.
create database staff_management;
use staff_management;
create table Employee
(
eID int NOT NULL IDENTITY(1,1) PRIMARY KEY,
eName varchar(255) NOT NULL,
Job text NOT NULL,
Salary int NOT NULL,
Comm int,
hDate date NOT NULL,
dID int NOT NULL,
);
alter table Employee
add Mgr int;
insert into Employee(eName,Mgr, Job, Salary, Comm, hDate, dID)
values ('ken Adams', 1004, 'Salesman', 70000, 20000, '2008-04-12', 1),
('Ru Jones', 1004, 'Salesman', 65000, 15000, '2010-01-18', 1),
('Dhal Sim', 1006, 'Accountant', 88000, NULL, '2001-03-07', 2),
('Ellen Honda', 1006, 'Manager', 118000, NULL, '2001-03-17', 1),
('Mike Bal', 1006, 'Receptionist', 68000, NULL, '2006-06-21', 3),
('Martin Bison',NULL, 'CEO', 210000, NULL, '2010-07-12', 3),
('Shen Li', 1004, 'Salesman', 86000, 18000, '2014-09-18', 1),
('Zang Ross', 1004, 'Salesman', 65000, 10000, '2017-02-02', 1),
('Sagar Kahn', 1004, 'Salesman', 70000, 15000, '2016-03-01', 1);
--alter table Employee
--add constraint emp_mgr_fk
-- foreign key (Mgr) references Employee(eID)
-- on update cascade on delete set NULL;
create table Department
(
dID int NOT NULL IDENTITY(1,1) PRIMARY KEY,
dName varchar(255) not null,
Address text,
phone text,
);
--alter table Employee
--add constraint emp_d_fk
--foreign key (dID) references Department(dID);
create table Grade
(
gID char(10) not null unique,
MinSal int,
MaxSal int,
Leavee int,
constraint g_pk primary key (gID)
);
INSERT INTO Grade (gID, MinSal, MaxSal, Leavee)
VALUES ('A', NULL, 60000, 20),
('B', 60000, 80000, 20),
('C', 80000, 100000, 20),
('D', 100000, 120000, 25),
('E', 120000, NULL, 30);
select * from Grade;
insert into Department (dName, Address, phone)
values ('Sales', 'Sydney', '0425 198 053'),
('Accounts', 'Melbourne', '0429 198 955'),
('Admin', 'Melbourne', '0428 198 758'),
('Marketing', 'Sydney', '0427 198 757');
select * from Department;

Filtering based on optional data

I have a messaging system with three tables: Event_Types, Messages, and Event_Subscription. All messages have an event type, and each event type can have 0 or more subscriptions. In the Event Subscription table are a set of columns that can be used to further filter the messages coming in. So if i set Src to a non-null value in Event_Subscription, I am saying that I only want messages with that event types from that particular source.
Currently, our query is writer such that for each column, we check if it's null, and if it isn't we filter the message by that column. I am fairly certain that this isn't going to perform well, especially since our actual prod version will have 11 different columns to filter by.
My question is, is there a more performant way to filter the messages table by the event subscriptions table?
example
IF OBJECT_ID('tempdb..#event_types') IS NOT NULL
DROP TABLE #event_types
IF OBJECT_ID('tempdb..#messages') IS NOT NULL
DROP TABLE #messages
IF OBJECT_ID('tempdb..#event_subscription') IS NOT NULL
DROP TABLE #event_subscription
CREATE TABLE #event_types
(
PK_EventTypeID INT IDENTITY(1, 1) PRIMARY KEY,
EventTypeName VARCHAR(50)
)
CREATE TABLE #messages
(
PK_MessageID INT IDENTITY(1, 1) PRIMARY KEY,
FK_EventTypeID INT NOT NULL,
Src VARCHAR(50),
[Version] VARCHAR(50)
)
CREATE TABLE #event_subscription
(
PK_EventSubscriptionID INT IDENTITY(1, 1) PRIMARY KEY,
FK_EventTypeID INT NOT NULL,
SubScriberID INT NOT null,
Src VARCHAR(50) null,
[Version] VARCHAR(50) null
)
INSERT INTO #event_types ( EventTypeName )
VALUES ('Insert'), ('Update'), ('Delete')
INSERT INTO #event_subscription
(
FK_EventTypeID,
SubScriberID,
Src,
[Version]
)
VALUES
( 1, 1000, null, null ), /* all inserts */
( 2, 1001, 'System A', '1.0.0' ) /*All updates from System A with version 1.0.0*/
INSERT INTO #messages ( FK_EventTypeID, Src, Version )
VALUES
( 1, 'System A', '1.0.0' ),
( 1, 'System B', '2.0.0' ),
( 2, 'System A', '1.0.0' ),
( 2, 'System B', '2.0.0' )
SELECT *
FROM #messages m
INNER JOIN #event_types et
ON m.FK_EventTypeID = et.PK_EventTypeID
INNER JOIN #event_subscription es
ON m.FK_EventTypeID = es.FK_EventTypeID
WHERE (es.Src IS NULL OR m.Src = es.Src)
AND (es.[Version] IS NULL OR m.[Version] = es.[Version])

Cardinality Estimate warning when a function(IS_MEMBER ) is used in where clause in SQLServer

Consider the following Scenarios
Test data
CREATE TABLE T1 (
COL1 numeric (6, 0) NOT NULL,
COL2 numeric (18, 0) NOT NULL,
COL3 numeric (18, 0) NOT NULL,
COL4 numeric (5, 0) NOT NULL,
COL5 numeric (18, 0) NOT NULL,
COL6 varchar (20) NOT NULL,
COL7 varchar (50) NULL,
COL8 numeric (1, 0) NULL,
COL9 numeric (18, 0) NULL,
COL10 varchar (20) NULL
)
ALTER TABLE T1
ADD PRIMARY KEY ( COL1,COL2, COL3, COL4, COL5, COL6 )
INSERT INTO T1 VALUES (1, 2, 12, 1, 11, 'COL_1', '000002', 0, NULL, 'admin_group')
INSERT INTO T1 VALUES (1, 2, 12, 1, 11, 'COL_2', '000002', 0, NULL, 'admin_group')
INSERT INTO T1 VALUES (1, 2, 12, 1, 11, 'COL_3', '000002', 0, NULL, 'QABrowns')
INSERT INTO T1 VALUES (1, 2, 12, 1, 11, 'COL_4', '000002', 0, NULL, 'QABrowns')
INSERT INTO T1 VALUES (1, 2, 12, 1, 11, 'COL_5', '000002', 0, NULL, 'QABrowns')
INSERT INTO T1 VALUES (1, 2, 12, 1, 11, 'COL_6', '000002', 0, NULL, 'QABrowns')
INSERT INTO T1 VALUES (1, 2, 12, 1, 11, 'COL_7', '000002', 0, NULL, 'QABrowns')
INSERT INTO T1 VALUES (1, 2, 12, 1, 11, 'COL_8', '000002', 0, NULL, 'SuperUser')
INSERT INTO T1 VALUES (1, 2, 12, 1, 11, 'COL_9', '000002', 0, NULL, 'SuperUser')
INSERT INTO T1 VALUES (1, 2, 12, 1, 11, 'COL_10', '000002', 0, NULL, 'SuperUser')
INSERT INTO T1 VALUES (1, 2, 12, 1, 11, 'COL_11', '000002', 0, NULL, 'LOLCOP2')
INSERT INTO T1 VALUES (1, 2, 12, 1, 11, 'COL_12', '000002', 0, NULL, 'LOLCOP2')
INSERT INTO T1 VALUES (1, 2, 12, 1, 11, 'COL_13', '000002', 0, NULL, 'LOLCOP2')
INSERT INTO T1 VALUES (1, 2, 12, 1, 11, 'COL_14', '000002', 0, NULL, 'LOLCOP2')
Scenario 1:
SELECT T1.* from T1
WHERE IS_MEMBER( COL10)=1
--Execution Plan warning--
"Type conversion in expression (CONVERT_IMPLICIT(nvarchar(20),[V9WEBDB1].[V9WEBDB1].[T3].[COL10],0)) may affect "CardinalityEstimate" in query plan choice"
Scenario 2
SELECT * INTO T3 FROM T1 WHERE COL10 IN ('admin_group')
ALTER TABLE T3
ADD PRIMARY KEY ( COL1,COL2, COL3, COL4, COL5, COL6 )
UPDATE STATISTICS T3
INSERT INTO T3 SELECT * FROM T1 WHERE COL10 IN ('LOLCOP2','QABrowns','SuperUser')
UPDATE STATISTICS T3
SELECT T3.* from T3
WHERE IS_MEMBER( COL10)=1
--Execution Plan warnings :
"Type conversion in expression (CONVERT_IMPLICIT(nvarchar(20),[V9WEBDB1].[V9WEBDB1].[T3].[COL10],0)) may affect "CardinalityEstimate" in query plan choice"
Scenario 3
SELECT * INTO T4 FROM T1 WHERE COL10 IN ('admin_group')
ALTER TABLE T4
ADD PRIMARY KEY ( COL1,COL2, COL3, COL4, COL5, COL6 )
UPDATE STATISTICS T4
SELECT T4.* from T4
WHERE IS_MEMBER( COL10)=1
Execution Plan warnings--None
INSERT INTO T4 SELECT * FROM T1 WHERE COL10 IN ('LOLCOP2','QABrowns','SuperUser')
UPDATE STATISTICS T4
SELECT T4.* from T4
WHERE IS_MEMBER( COL10)=1
Execution Plan warnings--None
ASK:
In 1st and 2nd scenarios
SELECT T1.* from T1 WHERE IS_MEMBER( COL10)=1
sql gives bellow warning
"Type conversion in expression (CONVERT_IMPLICIT(nvarchar(20),[V9WEBDB1].[V9WEBDB1].[T3].[COL10],0)) may affect "CardinalityEstimate" in query plan choice"
But in 3rd scenario
SELECT T1.* from T1 WHERE IS_MEMBER( COL10)=1
sql does not gives any warnings.
Data type of COL10 is varchar(20).
Server Collation : SQL_Latin1_General_CP1_CI_AS
1) Why does this warning happens?
2) How to avoid above warning?
1) Why does this warning happens?
Your table column col10 is varchar(20) but IS_MEMBER() accepts sysname as a parameter, so the server converts your varchar(20) to nvarchar(20) (sysname is nvarchar(128))
2) How to avoid above warning?
Change your col10 type to sysname or at least to nvarchar(20)

How to generate a unique ID for every insert statement in SQL Server?

I want to give a unique ID for every insert statement I make, this is so I can see what rows was inserted together. I prefer that the unique "insert ID" start at 1 and increases by one like the IDENTITY(1,1) for rows.
Is there a easy way like IDENTITY to do that?
CREATE TABLE [dbo].[factTrade]
(
[ID] [int] IDENTITY(1,1) NOT NULL,
[insertedID] [int] NOT NULL,
[quantity] [int] NOT NULL,
[price] [decimal](20, 10) NOT NULL
)
INSERT INTO [dbo].[factTrade] ([insertedID], [quantity], [price])
VALUES (1, 6, 2.5), (1, 4, 3.7), (1, 3, 4.1), (1, 7, 8.5),
INSERT INTO [dbo].[factTrade] ([insertedID], [quantity], [price])
VALUES (2, 5, 5.2), (2, 1, 4.6)
This isn't the solution you asked for, but you could add a column with a default timestamp to find all rows inserted at the same time.
ALTER TABLE dbo.factTrade
ADD InsertDate DATETIME NOT NULL DEFAULT (GETDATE())
Guids are handy for things like that.
declare #insertid uniqueidentifier = newid();
CREATE TABLE [dbo].[factTrade](
[ID] [int] IDENTITY(1,1) NOT NULL,
[insertedID] [int] NOT NULL,
[quantity] [int] NOT NULL,
[price] [decimal](20, 10) NOT NULL,
[insertid] [uniqueidentifier] not null
)
INSERT INTO [dbo].[factTrade]
([insertedID]
,[quantity]
,[price]
,[insertid]
)
VALUES
(1, 6, 2.5,#insertid),
(1, 4, 3.7,#insertid),
(1, 3, 4.1,#insertid),
(1, 7, 8.5,#insertid)
set #insertid = newid(); --get another guid
INSERT INTO [dbo].[factTrade]
([insertedID]
,[quantity]
,[price]
,[insertid]
)
VALUES
(2, 5, 5.2,#insertid),
(2, 1, 4.6,#insertid)
If you need an integer value, then you can create another table:
CREATE TABLE inserts ([ID] INT IDENTITY(1,1)...)
Then from your app, insert a row into this, then use the generated identity value (SCOPE_IDENTITY()).
INSERT inserts DEFAULT VALUES;
SELECT #insertId = SCOPE_IDENTITY();
This is what I ended up doing, thank you for all the suggestions and comments.
DECLARE #insertedID INT
CREATE TABLE [dbo].[factTrade]
(
[ID] [int] IDENTITY(1,1) NOT NULL,
[insertedID] [int] NOT NULL,
[quantity] [int] NOT NULL,
[price] [decimal](20, 10) NOT NULL
)
CREATE SEQUENCE [dbo].[factTradeInsertedID] AS INT
START WITH 1
INCREMENT BY 1
SET #insertedID = NEXT VALUE FOR [dbo].[factTradeInsertedID] --1
INSERT INTO [dbo].[factTrade]
([insertedID]
,[quantity]
,[price])
VALUES
(#insertedID, 6, 2.5)
,(#insertedID, 4, 3.7)
,(#insertedID, 3, 4.1)
,(#insertedID, 7, 8.5)
SET #insertedID = NEXT VALUE FOR [dbo].[factTradeInsertedID] --2
INSERT INTO [dbo].[factTrade]
([insertedID]
,[quantity]
,[price])
VALUES
(#insertedID, 5, 5.2)
,(#insertedID, 1, 4.6)

Select the column names from one table and Corresponding data from another table

I have 2 tables:
ColumnDefinition : column names will store
ColumnData: corresponding columniD(column name) we will insert the data.
I need one select statement with column names and with data as a DataSet.( out put may also XML )
TABLES:
FormColumDefinition
------------------------------
formColumnID
formColumnDataType
formColumnName
formColumnLabel
formColumnSeqNumber (just represents how it should appear on the form).
FormData
---------------
formDataID
formRowNumber
formColumnID
formDataDate (date) - only one of the three date/int/char will have value based on the column data type.
formDataInt (int)
formDataChar (varchar)
If I understand you correctly, given the following:
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
SET ANSI_PADDING ON
GO
CREATE TABLE [dbo].[FormData](
[formDataID] [int] IDENTITY(1,1) NOT NULL,
[formRowNumber] [int] NOT NULL,
[formColumnID] [int] NOT NULL,
[formDataDate] [datetime] NULL,
[formDataInt] [int] NULL,
[formDataChar] [varchar](50) NULL
) ON [PRIMARY]
GO
SET ANSI_PADDING OFF
GO
SET IDENTITY_INSERT [dbo].[FormData] ON
INSERT [dbo].[FormData] ([formDataID], [formRowNumber], [formColumnID], [formDataDate], [formDataInt], [formDataChar]) VALUES (1, 1, 1, NULL, NULL, N'John')
INSERT [dbo].[FormData] ([formDataID], [formRowNumber], [formColumnID], [formDataDate], [formDataInt], [formDataChar]) VALUES (2, 1, 2, NULL, NULL, N'Private')
INSERT [dbo].[FormData] ([formDataID], [formRowNumber], [formColumnID], [formDataDate], [formDataInt], [formDataChar]) VALUES (3, 1, 3, NULL, NULL, N'123456')
INSERT [dbo].[FormData] ([formDataID], [formRowNumber], [formColumnID], [formDataDate], [formDataInt], [formDataChar]) VALUES (4, 2, 1, NULL, NULL, N'Bill')
INSERT [dbo].[FormData] ([formDataID], [formRowNumber], [formColumnID], [formDataDate], [formDataInt], [formDataChar]) VALUES (5, 2, 2, NULL, NULL, N'Captain')
INSERT [dbo].[FormData] ([formDataID], [formRowNumber], [formColumnID], [formDataDate], [formDataInt], [formDataChar]) VALUES (6, 2, 3, NULL, NULL, N'789352')
SET IDENTITY_INSERT [dbo].[FormData] OFF
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
SET ANSI_PADDING ON
GO
CREATE TABLE [dbo].[FormColumnDefinition](
[formColumnID] [int] IDENTITY(1,1) NOT NULL,
[formColumnDataType] [varchar](50) NOT NULL,
[formColumnName] [varchar](50) NOT NULL,
[formColumnLabel] [varchar](50) NOT NULL,
[formColumnSeqNumber] [int] NOT NULL
) ON [PRIMARY]
GO
SET ANSI_PADDING OFF
GO
SET IDENTITY_INSERT [dbo].[FormColumnDefinition] ON
INSERT [dbo].[FormColumnDefinition] ([formColumnID], [FormColumnDataType], [formColumnName], [formColumnLabel], [formColumnSeqNumber]) VALUES (1, N'System.String', N'Name', N'Name', 0)
INSERT [dbo].[FormColumnDefinition] ([formColumnID], [FormColumnDataType], [formColumnName], [formColumnLabel], [formColumnSeqNumber]) VALUES (2, N'String.String', N'Rank', N'Rank', 1)
INSERT [dbo].[FormColumnDefinition] ([formColumnID], [FormColumnDataType], [formColumnName], [formColumnLabel], [formColumnSeqNumber]) VALUES (3, N'System.Int32', N'SerialNumber', N'Serial Number', 2)
SET IDENTITY_INSERT [dbo].[FormColumnDefinition] OFF
You could use a pivot command like:
SELECT formRowNumber, [Name],[Rank],[SerialNumber]
FROM
(SELECT fcd.formColumnName, fd.formRowNumber, fd.formDataChar
FROM FormColumnDefinition fcd INNER JOIN FormData fd
ON fcd.formColumnID = fd.formColumnID) AS src
PIVOT
(
MAX(formDataChar)
FOR formColumnName IN ([Name],[Rank],[SerialNumber])
) AS pvt
ORDER BY pvt.formRowNumber
to get:
formRowNumber Name Rank SerialNumber
1 John Private 123456
2 Bill Captain 789352
The problem with PIVOT is that you have to know the column names ahead of time. If you don't mind a little dynamic SQL, you can work around this too. Here's an example that I shamelessly stole from Andras at http://www.simple-talk.com/community/blogs/andras/archive/2007/09/14/37265.aspx.
DECLARE #cols NVARCHAR(2000);
SELECT #cols = STUFF(( SELECT DISTINCT TOP 100 PERCENT
'],[' + formColumnName
FROM FormColumnDefinition
ORDER BY '],[' + formColumnName
FOR XML PATH('')
), 1, 2, '') + ']';
DECLARE #query NVARCHAR(4000);
SET #query = N'SELECT formRowNumber, ' + #cols +
'FROM (SELECT fcd.formColumnName, fd.formRowNumber, fd.formDataChar ' +
'FROM FormColumnDefinition fcd INNER JOIN FormData fd ' +
'ON fcd.formColumnID = fd.formColumnID) AS src PIVOT ' +
'(MAX(formDataChar) FOR formColumnName IN ('+ #cols + ')) AS pvt ORDER BY pvt.formRowNumber;';
EXECUTE(#query);

Resources