Dynamic Sql Pivot Two Columns of Table - sql-server

Using Dynamic Pivots Tables, I'm trying to get this table: http://www.sqlfiddle.com/#!18/9f1cf/47
To look something like this: (Some Columns removed for brevity, assume I can have one or more columns past "Chosen Council", this is the expected design only not the expected result)
Note that Zip codes can be null, can share Councils, and can repeat over the days
+============+=======+================+============================+====================================+=========================+=====================+
| Call Date | Zip | Chosen Council | Early Childhood Group Care | Development / Developmental Delays | Caregiver Mental Health | Behavioral Concerns |
+============+=======+================+============================+====================================+=========================+=====================+
| 2018-05-01 | 85000 | Maricopa North | null | 1 | 2 | null |
+------------+-------+----------------+----------------------------+------------------------------------+-------------------------+---------------------+
| 2018-05-01 | 85001 | Maricopa North | 1 | null | null | null |
+------------+-------+----------------+----------------------------+------------------------------------+-------------------------+---------------------+
| 2018-05-01 | null | null | null | 2 | null | null |
+------------+-------+----------------+----------------------------+------------------------------------+-------------------------+---------------------+
| 2018-05-02 | 85000 | Maricopa North | null | 1 | 1 | 3 |
+------------+-------+----------------+----------------------------+------------------------------------+-------------------------+---------------------+
| 2018-05-02 | 85003 | Phoenix South | null | null | null | 2 |
+------------+-------+----------------+----------------------------+------------------------------------+-------------------------+---------------------+
| 2018-05-02 | 85004 | Phoenix South | 1 | 2 | null | 2 |
+------------+-------+----------------+----------------------------+------------------------------------+-------------------------+---------------------+
| 2018-05-02 | null | null | null | 1 | 1 | null |
+------------+-------+----------------+----------------------------+------------------------------------+-------------------------+---------------------+
I've seen a number of questions regarding Pivot Tables, both hard coded and dynamic, and I'm still not grasping it.
Here, I was able to get a Dynamic Pivot Table for just the Call Topic Names and their Counts: http://www.sqlfiddle.com/#!18/9f1cf/39
But that is only a single row for everything, it also seems to be ignoring nulls.
Here I tried to expand on the above, and while it seems to be spacing out better, I haven't figured out how to attach my Call Date, Zip, or Chosen Council columns: http://www.sqlfiddle.com/#!18/9f1cf/37
Any ideas how I can do this?
ASCII Table made with: Made with https://ozh.github.io/ascii-tables/

Maybe you need something like below
DECLARE #columns NVARCHAR(MAX), #sql NVARCHAR(MAX);
SET #columns = N'';
SELECT #columns += N', _callTopics.' + QUOTENAME(Name)
FROM
(
SELECT
_callTopics.Name
FROM CallTopics AS _callTopics
INNER JOIN CallTopicsPerRegion AS _callTopicsPerRegion
ON _callTopics.Name = _callTopicsPerRegion.CallTopicName
GROUP BY _callTopics.Name
) AS x;
SET #sql = N'
SELECT CallDate
,Zip
,ChosenCouncil, ' + STUFF(#columns, 1, 2, '') + '
FROM
(
SELECT _callTopics.Name, _callTopicsPerRegion.CallTopicCount,
CallDate
,Zip
,ChosenCouncil
FROM CallTopics AS _callTopics
INNER JOIN CallTopicsPerRegion AS _callTopicsPerRegion
ON _callTopics.Name = _callTopicsPerRegion.CallTopicName
) AS j
PIVOT
(
SUM(CallTopicCount) FOR Name IN ('
+ STUFF(REPLACE(#columns, ', _callTopics.[', ',['), 1, 1, '')
+ ')
) AS _callTopics order by 1,2 ,3';
--PRINT #sql;
EXEC sp_executesql #sql;
Here's fiddle link

Related

How can to get distinct data of all the columns in a table in spearte result set in SQL

I have created a Stored Procedure to get distinct data of all the columns. But I have to Specify each column name of the respective table.
Bur I don't want to specify each column name of the table and get the distinct data of all the columnn in separte result set.
+----+------+---------+-----------+
| Id | name | Address | City |
+----+------+---------+-----------+
| 1 | A | Max | Rajasthan |
| 2 | A | Min | Delhi |
| 1 | A | Max | Rajathan |
| 1 | A | Min | UP |
+----+------+---------+-----------+
This is the code of my Stored Procedure for getting different result set of each column
create proc sp_task1 #table varchar(20)
as
begin
exec('
select distinct id FROM ' +#table+'
')
exec('
select distinct name FROM ' +#table+'
')
exec('
select distinct address FROM ' +#table+'
')
exec('
select distinct city FROM ' +#table+'
')
end
exec sp_task1 #table = 'table1'
This is what I get in result when I Execute the SP.
+----+
| id |
+----+
| 1 |
| 2 |
+----+
+------+
| name |
+------+
| A |
+------+
+---------+
| Address |
+---------+
| Max |
| Min |
+---------+
+-----------+
| city |
+-----------+
| Rajasthan |
| Delhi |
+-----------+
Now, I want to do this dynamically without specifying the column names.
Please give me any kind of help regarding this issue.
You can use below query to do this.
TEST SETUP
CREATE TABLE Test(id int, name varchar(20), city varchar(20));
INSERT INTO Test
values(1,'abc','chennai'),
(2,'abc','bangalore');
CREATE PROCEDURE USP_GetDistinct(#TableName SYSNAME,#TableSchema SYSNAME )
AS
BEGIN
DECLARE #SQL NVARCHAR(MAX)
SET #SQL = (
SELECT 'SELECT DISTINCT ' +
Column_Name +
' FROM ' + QUOTENAME(TABLE_SCHEMA) + '.' + QUOTENAME(Table_Name) + CHAR(13)+ CHAR(10) + 'GO'+CHAR(13)+ CHAR(10)
FROM INFORMATION_SCHEMA.COLUMNS C
WHERE TABLE_NAME = #TableName AND table_schema = #TableSchema
FOR XML PATH(''), Type
).value('.', 'varchar(max)')
--PRINT #SQL
EXECUTE (#SQL)
END
Execute procedure
EXEC USP_GetDistinct #TableName='Test', #TableSchema='dbo'
Result set
+----+
| id |
+----+
| 1 |
| 2 |
+----+
+------+
| name |
+------+
| abc |
+------+
+-----------+
| city |
+-----------+
| bangalore |
| chennai |
+-----------+

SQL Dynamic Pivot for Some Columns

I'm trying to create a dynamic pivot code, where some columns are pivoted while others are not. Currently, I have one row per parent-child combination. I want a table where it is one row for every parent, with columns for child1, child2, etc. Some parents have multiple children, so number of columns(children) per parent is dynamic. I already have a column ordering the children by birthdate, and I'm adding this "childnumber" to the child columns.
Here's the sample table I'm starting with:
http://sqlfiddle.com/#!18/990f6/2
+===============+===========+-=============+=============+================+=============+==============+
| parent | parentage | parentgender | childname | childbirthdate | childgender | childnumber |
+===============+===========+==============+=============+================+=============+==============+
| John Smith | 32 | M | Jane Smith | 2005-05-21 | F | 1 |
| John Smith | 32 | M | Billy Smith | 2010-01-01 | M | 2 |
| Katherine Doe | 40 | F | Drew Fine | 2015-08-09 | M | 1 |
| Paula Lee | 28 | F | Peter Lee | 2009-12-30 | M | 1 |
| Paula Lee | 28 | F | Tim Lee | 2013-10-15 | M | 2 |
| Paula Lee | 28 | F | Andrew Lee | 2014-06-27 | M | 3 |
+---------------+-----------+--------------+-------------+----------------+-------------+--------------+
The final result should be:
+===============+===========+==============+============+=================+==============+=============+=================+==============+============+=================+===============+
| parent | parentage | parentgender | childname1 | childbirthdate1 | childgender1 | childname2 | childbirthdate2 | childgender2 | childname3 | childbirthdate3 | childgender3 |
+===============+===========+==============+============+=================+==============+=============+=================+==============+============+=================+===============+
| John Smith | 32 | M | Jane Smith | 2005-05-21 | F | Billy Smith | 2010-01-01 | M | null | null | null |
| Katherine Doe | 40 | F | Drew Fine | 2015-08-09 | M | null | null | null | null | null | null |
| Paula Lee | 28 | F | Peter Lee | 2009-12-30 | M | Tim Lee | 2013-10-15 | M | Andrew Lee | 2014-06-27 | M |
+---------------+-----------+--------------+------------+-----------------+--------------+-------------+-----------------+--------------+------------+-----------------+---------------+
My SQL Server Code Attempt:
IF OBJECT_ID('finaltable', 'U') IS NOT NULL DROP TABLE finaltable
DECLARE #columns AS NVARCHAR(MAX), #query AS NVARCHAR(MAX)
SELECT #columns = STUFF((SELECT DISTINCT ',' + QUOTENAME(col + '_' + CAST(childnumber as varchar(50)))
FROM
families
FOR XML PATH(''), TYPE ).value('.', 'NVARCHAR(MAX)'), 1, 1,'')
SET #query = 'SELECT parent, ' + #columns + '
FROM (
SELECT parent, parentage, parentgender, num = col+''_''+ CAST(childnumber as varchar(50))
FROM (
SELECT parent, childnumber, childname, childbirthdate, childgender
FROM families
) AS x
) AS source
PIVOT
(
MAX(childnumber)
FOR num in (' + #columns + ')
) AS pvt '
execute(#query);
I can't get this query to execute, and I'm not sure if my issue is with defining 'col' or if there are additional problems such as needing to unpivot first. Any help is greatly appreciated.

Formatting a pivot result by merging rows in Sql

The background:
Think of an application that lets people make surveys with custom questions, In a particular case, interview families, An
interviewer goes to House 1 and interviews two members Member 1
and Member 2. He asks questions like. What is this house
address?,What is your name and age?. The answers for
that is common for the Members and the answers that are specific for
them are stored in the same table
After doing some Joining on some tables and pivoting the result I end up getting the following table structure.
What was achieved so far
| ID | ADDRESS | MEMBER | AGE | SubformIteration |
|----|---------|----------|--------|-------------------|
| 1 | HOUSE 1 | (null) | (null) | (null) |
| 1 | (null) | MEMBER h | 18 | s0 |
| 1 | (null) | MEMBER i | 19 | s1 |
| 2 | HOUSE 2 | (null) | (null) | (null) |
| 2 | (null) | MEMBER x | 36 | s0 |
| 2 | (null) | MEMBER y | 35 | s1 |
| 3 | HOUSE 3 | (null) | (null) | (null) |
| 3 | (null) | MEMBER a | 18 | s0 |
| 3 | (null) | MEMBER b | 19 | s1 |
I am trying to find a way to get the table to be formatted as below:
Desired output
| ID | ADDRESS | MEMBER | AGE | SubformIteration |
|----|---------|----------|--------|-------------------|
| 1 | HOUSE 1 | MEMBER 1 | 18 | s0 |
| 1 | HOUSE 1 | MEMBER 2 | 19 | s1 |
| 2 | HOUSE 2 | MEMBER x | 36 | s0 |
| 2 | HOUSE 2 | MEMBER y | 35 | s1 |
| 3 | HOUSE 3 | MEMBER a | 18 | s0 |
| 3 | HOUSE 3 | MEMBER b | 19 | s1 |
I do not have enough sql vocabulary to describe and search the operation/procedure required to so As I am new to SQL and I would be really thankful if anybody could tell me an efficient way to achieve this.
Important
DO NOT RELY UPON THE QuestionText column as it will be changes When somebody decided to change the questions
Edit
Source tables
Sql fiddle link with all the below tables
As per the suggestions in the answers, I am posting the source table and the queries in hope that there will be a better understanding of the problem
Questions table
+------------+--------------+---------+----------+---------------+
| QuestionID | QuestionText | type | SurveyID | IsIncremental |
+------------+--------------+---------+----------+---------------+
| 3483 | subform | subform | 311 | 1 |
| 3484 | MEMBER | text | 311 | 0 |
| 3485 | AGE | number | 311 | 0 |
| 3486 | ADDRESS | address | 311 | 0 |
+------------+--------------+---------+----------+---------------+
Results table
+----------+-------------------------+----------+
| ResultID | DateSubmitted | SurveyID |
+----------+-------------------------+----------+
| 2272 | 2017-04-12 05:11:41.477 | 311 |
| 2273 | 2017-04-12 05:12:22.227 | 311 |
| 2274 | 2017-04-12 05:13:02.227 | 311 |
+----------+-------------------------+----------+
Chunks table, where all the answers are stored:
+---------+------------+----------+------------+------------------+
| ChunkID | Answer | ResultID | QuestionID | SubFormIteration |
+---------+------------+----------+------------+------------------+
| 9606 | HOUSE 1 | 2272 | 3486 | NULL |
| 9607 | MEMEBER 1 | 2272 | 3484 | NULL |
| 9608 | 12 | 2272 | 3485 | NULL |
| 9609 | MEMBER 2 | 2272 | 3484 | s1 |
| 9610 | 10 | 2272 | 3485 | s1 |
| 9611 | MEMEBER 1 | 2272 | 3484 | s0 |
| 9612 | 12 | 2272 | 3485 | s0 |
| 9613 | MEMBER 2 | 2272 | 3484 | s1 |
| 9614 | 10 | 2272 | 3485 | s1 |
| 9615 | HOUSE 2 | 2273 | 3486 | NULL |
| 9616 | MEMBER A | 2273 | 3484 | NULL |
| 9617 | 23 | 2273 | 3485 | NULL |
| 9618 | MEMBER B | 2273 | 3484 | s1 |
| 9619 | 25 | 2273 | 3485 | s1 |
| 9620 | MEMBER A | 2273 | 3484 | s0 |
| 9621 | 23 | 2273 | 3485 | s0 |
| 9622 | MEMBER B | 2273 | 3484 | s1 |
| 9623 | 25 | 2273 | 3485 | s1 |
| 9624 | HOUSE 3 | 2274 | 3486 | NULL |
| 9625 | MEMBER K | 2274 | 3484 | NULL |
| 9626 | 41 | 2274 | 3485 | NULL |
| 9627 | MEMBER J | 2274 | 3484 | s1 |
| 9628 | 26 | 2274 | 3485 | s1 |
| 9629 | MEMBER K | 2274 | 3484 | s0 |
| 9630 | 41 | 2274 | 3485 | s0 |
| 9631 | MEMBER J | 2274 | 3484 | s1 |
| 9632 | 26 | 2274 | 3485 | s1 |
+---------+------------+----------+------------+------------------+
I've written the following stored procedure which yields the first ever table given in this question:
ALTER PROCEDURE [dbo].[ResultForSurvey] #SurveyID int
AS
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX),#colsAggregated as nvarchar(max);
IF OBJECT_ID('tempdb.dbo.#Temp', 'U') IS NOT NULL
DROP TABLE #Temp;
SELECT *
INTO #Temp
FROM (Select Answer=( case
When Questions.type='checkboxes' or Questions.IsIncremental=1 THEN STUFF((SELECT distinct ',' + c.Answer
FROM Chunks c Where c.ResultID=Results.ResultID and c.QuestionID=Questions.QuestionID and (Chunks.SubFormIteration IS NULL )
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
else Chunks.Answer end),Chunks.SubFormIteration,Questions.QuestionText,Questions.type,Questions.QuestionID,Chunks.ResultID,Results.ResultID as Action,Results.DateSubmitted,Results.Username,Results.SurveyID from Chunks Join Questions on Questions.QuestionID= Chunks.QuestionID Join Results on Results.ResultID=Chunks.ResultID Where Results.SurveyID=#SurveyID) as X
SET #colsAggregated = STUFF((SELECT distinct ','+ 'max('+ QUOTENAME(c.QuestionText)+') as '+ QUOTENAME(c.QuestionText)
FROM #Temp c
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
print #colsAggregated
SET #cols = STUFF((SELECT distinct ',' + QUOTENAME(c.QuestionText)
FROM #Temp c
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = 'SELECT ResultID,max(Username) as Username,max(DateSubmitted) as DateSubmitted,max(SubFormIteration) as SubFormIteration, ' + #colsAggregated + ' from
(
select *
from #Temp
) as y
pivot
(
max(Answer)
for QuestionText in (' + #cols + ')
) as p GROUP BY
ResultID,SubFormIteration'
execute(#query)
It may be beneficial to post the query that got you your original results; there is a possibility that the original query could be rewritten to avoid this complexity. With the given information, this is the most simplistic way of solving this problem:
SELECT
h1.Id,
h2.Address,
h1.Member,
h1.Age,
h1.MemberNo
FROM House h1
INNER JOIN House h2
ON h1.Id = h2.Id
WHERE h2.Address IS NOT NULL -- Eliminates the results whre the Address is NULL after the join
AND h1.Member IS NOT NULL -- Eliminates the results that would show up from the original table (t1) where there is no Member field
Update:
Here is a simple example of the table structure using temp tables:
DROP TABLE #Questions
DROP TABLE #Results
DROP TABLE #Chunks
CREATE TABLE #Questions
(
QuestionId INT,
QuestionText VARCHAR(MAX),
type VARCHAR(MAX),
SurveyID INT,
IsIncremental INT
)
CREATE TABLE #Results
(
ResultId INT,
DateSubmitted DATETIME,
SurveyID INT
)
CREATE TABLE #Chunks
(
ChunkId INT,
Answer VARCHAR(MAX),
ResultId INT,
QuestionId INT,
SubFormIteration VARCHAR(20)
)
INSERT INTO #Results
VALUES (2272, '04-12-2017', 311),
(2273, '04-12-2017', 311),
(2274, '04-12-2017', 311)
INSERT INTO #Chunks
VALUES (9606, 'WhiteHouse', 2272, 3486, NULL),
(9607, 'MEMBER 1', 2272, 3484, NULL),
(9608, '12', 2272, 3485, NULL),
(9609, 'MEMBER 2', 2272, 3484, 's1'),
(9610, '10', 2272, 3485, 's1'),
(9611, 'MEMBER 1', 2272, 3484, 's0'),
(9612, '12', 2272, 3485, 's0'),
(9613, 'MEMBER 2', 2272, 3484, 's1'),
(9614, '10', 2272, 3485, 's1'),
(9615, 'RpBhavan', 2273, 3486, NULL),
(9618, 'MEMBER B', 2273, 3484, 's1'),
(9619, '25', 2273, 3485, 's1'),
(9620, 'MEMBER A', 2273, 3484, 's0'),
(9621, '23', 2273, 3485, 's0')
INSERT INTO #Questions
VALUES (3483, 'subform', 'subform', 311, 1),
( 3484, 'MEMBER', 'text', 311, 0 ),
(3485, 'AGE', 'number', 311, 0),
(3486, 'ADDRESS', 'address', 311, 0)
Here is a way to produce the results your looking for without the use of PIVOTs and XML:
; WITH Responses AS (
SELECT
c.ResultId,
QuestionText,
Answer,
c.SubFormIteration
FROM #Chunks c
INNER JOIN #Results r
ON c.ResultId = r.ResultId
INNER JOIN #Questions q
ON q.QuestionId = c.QuestionId
WHERE c.SubFormIteration IS NOT NULL -- Removes the "Address" responses and duplicate Answers
),
FindAddress AS (
-- Pulls ONLY the address for each ResultId
SELECT
ResultId,
MAX(CASE WHEN QuestionText = 'ADDRESS' THEN Answer END) AS [Address]
FROM #Chunks c
INNER JOIN #Questions q
ON q.QuestionId = c.QuestionId
GROUP BY ResultId
)
-- Combines all responses and the address back together
SELECT
r.ResultId,
fa.Address,
MAX(CASE WHEN QuestionText = 'MEMBER' THEN Answer END) AS [MEMBER],
MAX(CASE WHEN QuestionText = 'AGE' THEN Answer END) AS [Age],
SubFormIteration
FROM Responses r
INNER JOIN FindAddress fa
ON fa.ResultId = r.ResultId
GROUP BY r.ResultId, SubFormIteration, fa.Address
Essentially, I broke a rather large query into a Common Table Expression (CTE). Each query had a purpose: a) Response pulls all responses except the address, b) Pulls only the address based on ResultId, and c) Combine both queries together.
The MAX(CASE...) followed by GROUP BY is an alternative method to using PIVOTS and they essentially perform the same.
To apply this query to your specific case, you should only need to change the name of the tables.
As far as I understand: you want to do this dynamically. For this you need to prepare the question text and run it.
The columns are being prepared. Then merged with the query.
DECLARE #Columns NVARCHAR(MAX)
DECLARE #Query NVARCHAR(MAX)
SELECT #Columns = 'C.ResultId' +
(
SELECT
',' +
CASE WHEN COL.QuestionText = 'ADDRESS' THEN 'MAX(AA.Answer)' + COL.QuestionText
ELSE 'MAX(CASE WHEN Q.QuestionText = ''' + COL.QuestionText + ''' THEN C.Answer ELSE '''' END) AS ' + COL.QuestionText END
FROM
#Questions COL
WHERE
COL.QuestionText != 'subform'
FOR XML PATH ('')
) +
',MAX(C.SubFormIteration) AS SubFormIteration'
SET #Query = '
SELECT ' +
#Columns +
' FROM
#Chunks C INNER JOIN
#Results R ON C.ResultId = R.ResultId INNER JOIN
#Questions Q ON Q.QuestionId = C.QuestionId INNER JOIN
(
SELECT
IC.ResultId,
MAX(IC.Answer) AS Answer
FROM
#Chunks IC INNER JOIN
#Results IR ON IC.ResultId = IR.ResultId INNER JOIN
#Questions IQ ON IQ.QuestionId = IC.QuestionId
WHERE
IQ.QuestionText = ''ADDRESS''
GROUP BY
IC.ResultId
) AA ON C.ResultId = AA.ResultId
WHERE
C.SubFormIteration IS NOT NULL
GROUP BY
C.ResultId,
C.SubFormIteration
'
--SELECT #Query
EXEC sp_executesql #Query
Output:
ResultId MEMBER AGE ADDRESS SubFormIteration
----------- ----------- ---- ------------ --------------------
2272 MEMBER 1 12 WhiteHouse s0
2272 MEMBER 2 10 WhiteHouse s1
2273 MEMBER A 23 RpBhavan s0
2273 MEMBER B 25 RpBhavan s1
For Comment:
Columns "ResultId" and "SubFormIteration" are grouped and the result is. But the grouping operation is incorrect because the address information looks like this. The query and result are below.
ResultId MEMBER AGE ADDRESS SubFormIteration
----------- -------------------------------------------------------
2272 MEMBER 1 12 WhiteHouse NULL -- Which value you want to group. s1 or s0
2272 MEMBER 1 12 s0
2272 MEMBER 2 10 s1
2273 RpBhavan NULL -- Which value you want to group. s1 or s0
2273 MEMBER A 23 s0
2273 MEMBER B 25
Query:
DECLARE #Columns NVARCHAR(MAX)
DECLARE #Query NVARCHAR(MAX)
SELECT #Columns = 'C.ResultId' +
(
SELECT
',' +
'MAX(CASE WHEN Q.QuestionText = ''' + COL.QuestionText + ''' THEN C.Answer ELSE '''' END) AS ' + COL.QuestionText
FROM
#Questions COL
WHERE
COL.QuestionText != 'subform'
FOR XML PATH ('')
) +
',MAX(C.SubFormIteration
) AS SubFormIteration'
SET #Query = '
SELECT ' +
#Columns +
' FROM
#Chunks C INNER JOIN
#Results R ON C.ResultId = R.ResultId INNER JOIN
#Questions Q ON Q.QuestionId = C.QuestionId
GROUP BY
C.ResultId,
C.SubFormIteration
'
--SELECT #Query
EXEC sp_executesql #Query
If the above table is a result of multiple joins/etc, it would be better if we could give a recommendation based on the actual schema available to you. However, if the sample table in the SQL Fiddle link is all you have to work with, try the following:
SELECT h.address, p.member, p.age, p.memberno
FROM House h
INNER JOIN
House p
ON h.id = p.id
AND h.member IS NULL
AND p.member IS NOT NULL
We can't see your input query, but my guess is that you're getting those null columns because of a LEFT or RIGHT join in your source query. If you could split your result vertically into two views like this:
| ID | ADDRESS |
|----|---------|
| 1 | HOUSE 1 |
and
| ID | MEMBER | AGE | MEMBERNO |
|----|----------|--------|----------|
| 1 | MEMBER 1 | 18 | 1 |
| 1 | MEMBER 2 | 19 | 2 |
and then join them on ID field, you'll get precisely the result you need.
Edit
After looking at your edit, here is how you apply the above method in your scenario:
First query:
SELECT ID, ADDRESS FROM YourTable WHERE ADDRESS IS NOT NULL
Second query:
SELECT MEMBER, AGE, MEMBERNO WHERE MEMBER IS NOT NULL AND AGE IS NOT NULL AND MEMBERNO IS NOT NULL
Now join them together on ID:
SELECT * FROM
(SELECT ID, ADDRESS FROM YourTable WHERE ADDRESS IS NOT NULL) AS A
INNER JOIN
(SELECT MEMBER, AGE, MEMBERNO WHERE MEMBER IS NOT NULL AND AGE IS NOT NULL AND MEMBERNO IS NOT NULL) AS B
ON A.ID = B.ID
By Looking Of Your Answer i think you need result of CROSS JOIN of Two Table
You can use this query:
SELECT * from table1,table2
This will help you...

Generating unique identifiers as a set based 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.

SQL Pivot Table producing duplicates

Developers,
I am new to pivot tables, and am having a little problem with duplicates. My table, before pivoting looks like so:
location | food
Tennessee | pear
Tennessee | orange
Florida | orange
Florida | apple
Virginia | pear
Here is the code to pivot, which works fine:
SELECT PivotTable.location, [apple], [orange], [pear]
FROM
(SELECT location, food FROM someTable) as inventory
PIVOT
(COUNT(inventory.food) FOR inventory.location IN ([apple],[orange],[pear])) AS PivotTable
This produces an output like so:
Location | Apple | Orange | Pear
Tennessee | 0 | 1 | 1
Florida | 1 | 1 | 0
Virginia | 0 | 0 | 1
Which as I said works fine. However, I added new columns for comments to my original table, like so:
location | food | apple_comments | orange_comments | pear_comments
Tennessee | pear | NULL | NULL | NULL
Tennessee | orange | NULL | very juicy | NULL
Florida | orange | NULL | NULL | NULL
Florida | apple | crisp | NULL | NULL
Virginia | pear | NULL | NULL| tasty
Here is my altered pivot table to account for the comments:
SELECT PivotTable.location, [apple], [apple_comments], [orange], [orange_comments], [pear], [pear_comments]
FROM
(SELECT location, food, apple_comments, orange_comments, pear_comments FROM someTable) as inventory
PIVOT
(COUNT(inventory.food) FOR inventory.location IN ([apple],[orange],[pear])) AS PivotTable
This produces an output like so:
Location | Apple | apple_comments | Orange | Orange_comments | Pear | Pear_comments
Tennessee | 0 | NULL | 0 | NULL | 1 | NULL
Tennessee | 0 | NULL | 1 | very juicy | 0 | NULL
Florida | 0 | NULL | 1 | NULL | 0 | NULL
Florida | 1 | crisp | 1 | NULL | 0 | NULL
Virginia | 0 | NULL | 1 | NULL | 1 | tasty
So, essentially, it is creating a duplicate row when comments are added for each entry where there are multiple locations. In the case of Virginia, there is only one entry, so the row turns out fine.
It almost seems like I need to do another pivot or something. Can anyone offer advice on where I'm going wrong?
Sorry. The desired output should look like so:
Location | Apple | apple_comments | Orange | Orange_comments | Pear | Pear_comments
Tennessee | 0 | NULL | 1 | very juicy | 1 | NULL
Florida | 1 | crisp | 1 | NULL | 0 | NULL
Virginia | 0 | NULL | 1 | NULL | 1 | tasty
Essentially, merging the duplicates into one row.
Thanks.
The fundamental problem is that you have effectively told the compiler to group by the comment column in addition to the food column. There are some solutions such as rolling up the comments into a delimited list like so:
Select location
, Sum( Case When S.food = 'Apple' Then 1 Else 0 End ) As Apple
, Stuff(
(
Select ', ' + S1.Apple_Comments
From SomeTable As S1
Where S1.location = S.location
And S1.Apple_Comments Is Not Null
Group By S1.Apple_Comments
For Xml Path(''), type
).value('.','nvarchar(max)')
, 1, 2, '') As Apple_Comments
, Sum( Case When S.food = 'Orange' Then 1 Else 0 End ) As Orange
, Stuff(
(
Select ', ' + S1.Orange_Comments
From SomeTable As S1
Where S1.location = S.location
And S1.Orange_Comments Is Not Null
Group By S1.Orange_Comments
For Xml Path(''), type
).value('.','nvarchar(max)')
, 1, 2, '') As Orange_Comments
, Sum( Case When S.food = 'Pear' Then 1 Else 0 End ) As Pear
, Stuff(
(
Select ', ' + S1.Pear_Comments
From SomeTable As S1
Where S1.location = S.location
And S1.Pear_Comments Is Not Null
Group By S1.Pear_Comments
For Xml Path(''), type
).value('.','nvarchar(max)')
, 1, 2, '') As Pear_Comments
From SomeTable As S
Group By S.location
Found the answer (utilizes the 'with CTE' and MAX functions):
;With CTE as (
SELECT PivotTable.location, [apple], [apple_comments], [orange], [orange_comments], [pear], [pear_comments]
FROM
(SELECT location, food, apple_comments, orange_comments, pear_comments FROM someTable) as inventory
PIVOT
(COUNT(inventory.food) FOR inventory.location IN ([apple],[orange],[pear])) AS PivotTable)
select location, MAX([apple]) as [apple], MAX([apple_comments]) as [apple_comments],MAX([orange]) as [orange],
MAX([orange_comments]) as [orange_comments], MAX([pear]) as [pear], MAX([pear_comments]) as [pear_comments]
from CTE group by location

Resources