how create dynamic columns using tables data in sql server 2008? - dynamic

i have four tables like below in sql server 2008 :
TABLE 1 -> Users
UserID UserName
-----------------------
1 Jhon
TABLE 2 -> PhoneBook
PhonebookID UserID Name MobileNumber
-------------------------------------------------------------------
1 1 MyBrother 252848
TABLE 3 -> PhonebookExtraField
PhonebookExtraFieldID UserID ExtraFieldName
-------------------------------------------------------------
1 1 Age
2 1 Job
3 1 Address
TABLE 4 -> phoneBookExtraFieldData
phoneBookExtraFieldDataID PhonebookExtraFieldID PhonebookID ExtraFieldValue
-----------------------------------------------------------------------------------------
101 1 1 30
102 2 1 Web Developer
103 3 1 A.V. Rose
how can i write a query for output below :
mean i am looking for a way for creating dynamic columns using Tables data...
UserName Phonebook(Name) Phonebook(MobileNumber) Age Job Address
-------------------------------------------------------------------------------------
Jhon MyBrother 252848 30 Web Developer A.V. Rose
what is the best way for doing this job?
how should i change my tables for this purpose?
thanks for attention and advance...

Maybe something like this:
Test data
CREATE TABLE #User
(
UserID INT,
UserName VARCHAR(100)
)
INSERT INTO #User
VALUES(1,'Jhon')
CREATE TABLE #PhoneBook
(
PhonebookID INT,
UserID INT,
Name VARCHAR(100),
MobileNumber INT
)
INSERT INTO #PhoneBook
VALUES(1,1,'MyBrother',252848)
CREATE TABLE #PhonebookExtraField
(
PhonebookExtraFieldID INT,
UserID INT,
ExtraFieldName VARCHAR(100)
)
INSERT INTO #PhonebookExtraField
VALUES(1,1,'Age'),(2,1,'Job'),(3,1,'Address')
CREATE TABLE #PhoneBookExtraFieldData
(
PhoneBookExtraFieldDataID INT,
PhonebookExtraFieldID INT,
PhonebookID INT,
ExtraFieldValue VARCHAR(100)
)
INSERT INTO #PhoneBookExtraFieldData
VALUES(101,1,1,'30'),(102,2,1,'Web Developer'),(103,3,1,'A.V. Rose')
The find the dynamic columns
DECLARE #cols VARCHAR(MAX)
SELECT #cols = COALESCE(#cols + ','+QUOTENAME(ExtraFieldName),
QUOTENAME(ExtraFieldName))
FROM
#PhonebookExtraField
The execute some dynamic sql with a Pivot:
DECLARE #query NVARCHAR(4000)=
N'SELECT
*
FROM
(
SELECT
#User.UserName,
#PhoneBook.Name AS [Phonebook(Name)],
#PhoneBook.MobileNumber AS [Phonebook(MobileNumber)],
#PhonebookExtraField.ExtraFieldName,
#PhoneBookExtraFieldData.ExtraFieldValue
FROM
#User
JOIN #PhoneBook
ON #User.UserID=#PhoneBook.UserID
JOIN #PhonebookExtraField
ON #PhoneBook.UserID=#PhonebookExtraField.UserID
JOIN #PhoneBookExtraFieldData
ON #PhonebookExtraField.PhonebookExtraFieldID=#PhoneBookExtraFieldData.PhonebookExtraFieldID
) AS p
PIVOT
(
MAX(ExtraFieldValue)
FOR ExtraFieldName IN('+#cols+')
) AS pvt'
EXECUTE(#query)
Then I will clean up after myself:
DROP TABLE #User
DROP TABLE #PhoneBook
DROP TABLE #PhonebookExtraField
DROP TABLE #PhoneBookExtraFieldData

Select
*
from
user as u,
PhoneBook as pb,
PhonebookExtraField as pbf,
phoneBookExtraFieldData as pbdf
where
u.userid=pb.userID
and pb.userID=pbf.userID
and pbf.PhonebookExtraFieldID =pfbd.PhonebookExtraFieldID
and u.Username='Jhon'
Replace * with required column names.

Related

Insert - Select keeping identity mapping

I have 2 tables, and im trying to insert data from one to another and keepeng the mappings between ids.
I found here someone with the same problem, but the solution isnt good for me.
here is the example:
the two tables
CREATE TABLE [source] (i INT identity PRIMARY KEY, some_value VARCHAR(30))
CREATE TABLE [destination] (i INT identity PRIMARY KEY, some_value VARCHAR(30))
CREATE TABLE [mapping] (i_old INT, i_new INT) -- i_old is source.i value, i_new is the inserted destination.i column
some sample data
INSERT INTO [source] (some_value)
SELECT TOP 30 name
FROM sysobjects
INSERT INTO [destination] (some_value)
SELECT TOP 30 name
FROM sysobjects
Here, i want to transfer everything from source into destination, but be able to keep a mapping on the two tables:
I try to use OUTPUT clause, but i cannot refer to columns outside of the ones being inserted:
INSERT INTO [destination] (some_value)
--OUTPUT inserted.i, s.i INTO [mapping] (i_new, i_old) --s.i doesn't work
SELECT some_value
FROM [source] s
Anyone has a solution for this?
Not sure is it write way but it works :D
MERGE [#destination] AS D
USING [#source] AS s
ON s.i <> s.i
WHEN NOT MATCHED BY TARGET
THEN
INSERT (some_value) VALUES (some_value)
OUTPUT inserted.i, s.i INTO [#mapping] (i_new, i_old);
try this sql below if you don't have permission to modify the tables:
The idea is using a temp table to be a bridge between destination table and the mapping table.
SQL Query:
declare #source table (i INT identity PRIMARY KEY, some_value VARCHAR(30))
declare #destination table (i INT identity PRIMARY KEY, some_value VARCHAR(30))
declare #mapping table (i_old INT, i_new INT) -- i_old is source.i value, i_new is the inserted destination.i column
declare #tempSource table
(
id_source INT identity , source_value VARCHAR(30)
,Id_New int,source_new VARCHAR(30)
)
insert into #source
output inserted.i, inserted.some_value into #tempSource(id_source,source_value)
SELECT TOP 10 name
FROM sysobjects
--select * from #tempsource
insert into #destination
OUTPUT inserted.i, inserted.some_value INTO #tempSource (Id_New,source_new)
select source_value from #tempSource
insert into #mapping
select Id_source, Id_New from
(
select a.id_source, a.source_value
from
#tempSource a
where id_source is not null and source_value is not null
) aa
inner join
(
select a.Id_New, a.source_new
from
#tempSource a
where Id_New is not null and source_new is not null
) bb on aa.source_value = bb.source_new
select * from #mapping
The mapping table result:
i_old i_new
----------- -----------
1 1
2 2
3 3
4 4
5 5
6 6
7 7
8 8
9 9
10 10

Pivot or Cross Tab [duplicate]

This question already has answers here:
Pivot in sql server
(4 answers)
Closed 8 years ago.
I have the two tables which need to be linked and present the data in human friendly way. Could you experts point me in right direction, I am bit stuck here.
Both table 1 and table2 are received through ftp and loaded to SQL table in SQL 2008 R2. These two tables are linked by nid and cid together.
Apologies i couldn't copy paste table here, please consider "-" are column separators
Table 1
ID nid cid pid form_key-name
1 4 1 33 Import_Gen_Title-Title
2 4 2 33 Import_Gen_Firstname-Firstname
3 4 3 33 Import_Gen_Surname-Surname
4 4 4 33 Import_Gen_AddressLine1-AddressLine1
5 4 5 33 Import_Gen_AddressLine2-AddressLine2
6 4 6 33 Import_Gen_City-Town/City
7 4 7 33 Import_Gen_Zip-Post code
Table 2
ID nid sid cid data
1 4 14 1 Mr
2 4 14 2 John
3 4 14 3 Smith
4 4 14 4 A Company
5 4 14 5 Nice Street
6 4 14 6 London
7 4 14 7 SE11 0TS
Now how can get this a Result Table like this one below ?
NiD SID Title Firstname Surname AddressLine1 AddressLine2 Town/City-Post code
4 14 Mr John Smith A Company Nice Street London-SE11 0TS
Check my answer for this same question here: Pivot in sql server
I made a stored procedure that uses the PIVOT statement shown by salvoo - but it's more generic, you don't have to know all the columns in advance.
Take a look.
Here is the example using your data - note that the default output of the pivot has the columns in alphabetical order which you don't want - so I used the option to output the pivot to a temp table then queried directly from the temp table to put the columns in the order you wanted.
drop table Fields
go
drop table Data
go
create table Fields
(
ID Integer,
nid Integer,
cid Integer,
pid Integer,
form_key_name varchar(50)
);
go
create table Data
(
ID Integer,
nid Integer,
sid Integer,
cid Integer,
data varchar(50)
);
go
insert into Fields values (1,4,1,33,'Import_Gen_Title-Title')
go
insert into Fields values (2,4,2,33,'Import_Gen_Firstname-Firstname')
go
insert into Fields values (3,4,3,33,'Import_Gen_Surname-Surname')
go
insert into Fields values (4,4,4,33,'Import_Gen_AddressLine1-AddressLine1')
go
insert into Fields values (5,4,5,33,'Import_Gen_AddressLine2-AddressLine2')
go
insert into Fields values (6,4,6,33,'Import_Gen_City-Town/City')
go
insert into Fields values (7,4,7,33,'Import_Gen_Zip-Post code')
go
insert into Data values (1,4,14,1,'Mr')
go
insert into Data values (2,4,14,2,'John')
go
insert into Data values (3,4,14,3,'Smith')
go
insert into Data values (4,4,14,4,'A Company')
go
insert into Data values (5,4,14,5,'Nice Street')
go
insert into Data values (6,4,14,6,'London')
go
insert into Data values (7,4,14,7,'SE11 0TS')
go
declare #mySQL varchar(MAX);
set #mySQL = '
select
f.nid,
d.sid,
right(f.form_key_name, len(f.form_key_name) - charindex(''-'',f.form_key_name)) form_key_name,
d.data
from
Fields f
JOIN Data d
on ( d.nid = f.nid
and d.cid = f.cid )
';
exec pivot_query #mySQL, 'nid, sid', 'form_key_name','max(data)', '##tmppivot';
select
nid,
sid,
Title,
Firstname,
Surname,
AddressLine1,
AddressLine2,
[Town/City],
[Post code]
from
##tmppivot;
go
With Pivot: Fiddle demo
SELECT sid, nid,
[Import_Gen_Title-Title] as Title,
[Import_Gen_Firstname-Firstname] as Name,
[Import_Gen_Surname-Surname] as SurName,
[Import_Gen_AddressLine1-AddressLine1] as Address1,
[Import_Gen_AddressLine2-AddressLine2] as Address2,
[Import_Gen_City-Town/City] as Town,
[Import_Gen_Zip-Post code] as PostCode
FROM
(
SELECT t2.sid, t2.nid, t2.dat, t1.form_key
FROM tab1 t1 INNER JOIN tab2 t2 ON t1.nid = t2.nid AND t1.cid = t2.cid
) x
PIVOT
(
min(x.dat)
for x.form_key in ([Import_Gen_Title-Title],
[Import_Gen_Firstname-Firstname],
[Import_Gen_Surname-Surname],
[Import_Gen_AddressLine1-AddressLine1],
[Import_Gen_AddressLine2-AddressLine2],
[Import_Gen_City-Town/City],
[Import_Gen_Zip-Post code]
)
) pvt
Edit (Added generic version): Fiddle demo
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX);
SET #cols = STUFF((SELECT ',[' + t.form_key + ']'
FROM tab1 t
group by t.form_key, cid
order by t.cid
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)'),1,1,'');
SET #query = 'SELECT * FROM
(
SELECT t2.sid, t2.nid, t2.dat, t1.form_key
FROM tab1 t1 INNER JOIN tab2 t2 ON t1.nid = t2.nid AND t1.cid = t2.cid
) x
PIVOT
(
min(x.dat)
FOR x.form_key IN (' + #cols + ')
) pvt;';
execute(#query);

What the best way to get values in multiple rows and set variables with out cursors in SQL Server? (PIVOT more or less)

I could not think of a better question for the problem, but here it is.
I have 3 tables in a many to many relationship like:
Students -> StudentTasks <- Tasks
The student task has a column called "Marked", and when a student is created a create a record for possible tasks and Marked = 0, Ex:
Student ---------
Given
"StudentA -> Id = 1"
Tasks ------------
Given
"Task A -> Id = 1"
"Task B -> Id = 2"
StudentTasks ------
StudentId -- TaskId -- Marked
1 -- 1 -- 0
1 -- 2 -- 0
What I need is for each row in student task I have to set variable #TaskA, #TaskB
with the respective "Marked" value, so I can update another table's column with it.
Another way to solve the problem would be to update the table directly, so given the same scenario, but with the addition of a table like so:
StudentId -- TaskA -- TaskB
I would like to see it filled like this:
StudentId -- TaskA -- TaskB
1 -- 0 -- 0
and if we had "StudentB -< Id = 2" with TaskB marked a 1 we would have:
StudentId -- TaskA -- TaskB
1 -- 0 -- 0
2 -- 0 -- 1
The way I am doing is not efficient at all, its taking 40 seconds to go through 3300 records (I am using cursors to walk through the list of students), any suggestions are welcome.
UPDATES:
Using the idea #djangojazz did with the self extracting queries
DECLARE #Student TABLE ( StudentID INT IDENTITY, Name VARCHAR(50));
INSERT INTO #Student VALUES ('Brett'),('Sean')
DECLARE #StudentTasks TABLE (StudentID INT, TaskId INT, Marked BIT);
INSERT INTO #StudentTasks VALUES (1,1,0),(1,2,0),(2,1,0),(2,2,1)
DECLARE #Tasks TABLE (TaskID INT IDENTITY, NAME VARCHAR(50));
INSERT INTO #Tasks VALUES ('Study'),('Do Test')
SELECT * FROM #Student
SELECT * FROM #StudentTasks
SELECT * FROM #Tasks
-- THIS IS WHAT I NEED IN THE RESULT
DECLARE #ResultTable TABLE (StudentId INT, Study BIT, DoTest BIT);
INSERT INTO #ResultTable VALUES (1,0,0),(2,0,1)
SELECT * FROM #ResultTable
UPDATED 6-13-13. I don't even think you need the 'Students' table right off the bat as you just want to pivot on the identifier. The problem may become though if you use repeat values for anything in the future this logic will break. Meaning if you have a table where a Student Id may be repeated for another value with a type other than bit you would then need to perform more operations to see which was the most current by an identity or such. Saying that though I can give you what you say you wanted.
DECLARE #Student TABLE ( StudentID INT IDENTITY, Name VARCHAR(50));
INSERT INTO #Student VALUES ('Brett'),('Sean')
DECLARE #StudentTasks TABLE (StudentID INT, TaskId INT, Marked BIT);
INSERT INTO #StudentTasks VALUES (1,1,0),(1,2,0),(2,1,0),(2,2,1)
DECLARE #Tasks TABLE (TaskID INT IDENTITY, NAME VARCHAR(50));
INSERT INTO #Tasks VALUES ('Study'),('Do Test')
DECLARE #ResultTable TABLE (StudentId INT, Study BIT, DoTest BIT);
INSERT INTO #ResultTable VALUES (1,0,0),(2,0,1)
select 'Results you wanted'
SELECT *
FROM #ResultTable
select 'Method A'
;
-- with case when forcing a pivot on an expression
With x as
(
select
st.StudentID
, cast(st.Marked as tinyint) as Marked
, t.NAME
from #StudentTasks st
join #Tasks t on st.TaskId = t.TaskID
)
Select
x.StudentID
, max(case when NAME = 'Study' then Marked end) as Study
, max(case when NAME = 'Do Test' then Marked end) as DoTest
FROM x
group by x.StudentID
Select 'Method B'
;
-- with traditional pivot you need to translate names I believe
With x as
(
select
st.StudentID
, cast(st.Marked as tinyint) as Marked
, case when t.NAME = 'Study' then 0 else 1 end as Name
from #StudentTasks st
join #Tasks t on st.TaskId = t.TaskID
)
Select
pvt.StudentID
, [0] as Study
, [1] as 'Do Test'
FROM x
pivot(max(x.Marked) for x.Name in ([0], [1])) as pvt
PIVOT the StudentTasks table for the 2 Tasks - A and B, JOIN it with the 'Additional table' on StudentID and Update it ..

Select MAX and add 1 (SQL Server 2012)

DROP TABLE #ID
CREATE TABLE #ID (ID INT)
INSERT INTO #ID (ID)
VALUES (24),(65),(77),(44)
DECLARE #ID int
SELECT #ID = MAX(ID) from #ID
DROP TABLE #name
CREATE TABLE #NAME (Name char (20))
INSERT INTO #NAME (name)
VALUES ('Ben'),('Alex'),('Mark')
DROP TABLE #abc
CREATE TABLE #ABC (ID INT, Name char(20))
INSERT INTO #ABC (ID,Name)
SELECT #ID,name
FROM #name
SELECT * FROM #ABC
I want the process to pick up the maximum ID from #ID and then add 1 for the next record. So, expected result should be:
ID Name
77 Ben
78 Alex
79 Mark
Please help me getting around this logic without using cursors. Can I use IDENTITY(#ID,1) in any way? Thanks
Replace:
Select * from #ABC
For:
Select ROW_NUMBER() OVER (ORDER BY ID) + ID - 1, Name from #ABC
Working fiddle

How to show query result columnar(in different columns) instead of row by row?

I asked this question in a different post but i t has been changed.
I have three tables:
Flight table
FlightId int
FlightNumber varchar(10)
FlightCapacity table
ID int
FlightIdRef int
ClassIdRef int
Capacity int
Class Table
ClassId int
Name varchar(10)
Class Table:
ClassId Name
1 Y
2 A
Flight Table
FlightId Number
1 123
2 423
FlightCapacity Table
Id FlightIdRef ClassIdref Capacity
1 1 1 10
2 1 2 20
3 2 2 10
this is a simple query:
select Flight.FlightNumber,Class.Name+RTRIM(FlightCapacity.Capacity)
from Flight
inner join FlightCapacity
on Flight.FlightId=FlightCapacity.FlightIdRef
inner join Class
on FlightCapacity.ClassIdRef=Class.ClassId
but I want following result:(I want to show all classes of every flight and capacity in one single row but in different columns)
FlightNumber ClassNameAndCapacity1 ClassNameAndCapacity2 ClassNameAndCapacityn
123 Y10 A20
423 Y10 ---
Maybe something like this:
First some test data:
CREATE TABLE Flight(FlightId int,FlightNumber varchar(10))
CREATE TABLE FlightCapacity(ID int,FlightIdRef int,ClassIdRef int,Capacity int)
CREATE TABLE Class(ClassId int,Name varchar(10))
INSERT INTO Class VALUES(1,'Y'),(2,'A')
INSERT INTO Flight VALUES(1,123),(2,423)
INSERT INTO FlightCapacity VALUES(1,1,1,10),(2,1,2,20),(3,2,2,10)
Then you have to get the unique columns like this:
DECLARE #cols VARCHAR(MAX)
SELECT #cols = COALESCE(#cols + ','+
QUOTENAME('ClassNameAndCapacity'+CAST(ClassId AS VARCHAR(10))),
QUOTENAME('ClassNameAndCapacity'+CAST(ClassId AS VARCHAR(10))))
FROM
Class
The delcaring and execute the dynamic sql:
DECLARE #query NVARCHAR(4000)=
N'SELECT
*
FROM
(
SELECT
Flight.FlightNumber,
Class.Name+CAST(FlightCapacity.Capacity AS VARCHAR(100)) AS ClassName,
''ClassNameAndCapacity''+CAST(Class.ClassId AS VARCHAR(10)) AS ClassAndCapacity
FROM
Flight
JOIN FlightCapacity
ON Flight.FlightId=FlightCapacity.FlightIdRef
JOIN Class
ON FlightCapacity.ClassIdRef=Class.ClassId
) AS p
PIVOT
(
MAX(ClassName)
FOR ClassAndCapacity IN('+#cols+')
) AS pvt'
EXECUTE(#query)
And then in my case I will drop the tables created:
DROP TABLE Flight
DROP TABLE Class
DROP TABLE FlightCapacity