Extracting data column-wise from table in SQL Server - sql-server

I currently have a piece of code that pivots a table in which the row data is inserted dynamically. The code is shown below:
CREATE TABLE Table1
([empname] varchar(6), [empqual] varchar(10), [emprank] int, [empexp] int)
INSERT INTO Table1
([empname], [empqual], [emprank], [empexp])
VALUES
('Joyce', 'UNIVERSITY', 1, 11),
('Angela', 'MASTERS', 2, 10),
('Lily', 'MASTERS', 3, 9),
('Sasha', 'UNIVERSITY', 3, 9),
('Harry', 'UNIVERSITY', 3, 9)
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
SET #cols = STUFF((SELECT distinct ',' + 'Column' + CONVERT(VARCHAR,Row_Number() OVER (Order By emprank))
FROM Table1 c
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
SET #query = '
SELECT *
FROM
(
SELECT ''Column'' + CONVERT(VARCHAR,Row_Number() OVER (Order By emprank)) AS Columns,
CONVERT(VARCHAR,empname) as e
FROM Table1
) p
PIVOT
(
MAX (e)
FOR Columns IN
(
' + #cols + ' )
) as pvt
UNION
SELECT *
FROM
(
SELECT ''Column'' + CONVERT(VARCHAR,Row_Number() OVER (Order By emprank)) AS Columns,
CONVERT(VARCHAR,empqual) as e
FROM Table1
) p
PIVOT
(
MAX (e)
FOR Columns IN
(
' + #cols + ' )
) as pvt
UNION
SELECT *
FROM
(
SELECT ''Column'' + CONVERT(VARCHAR,Row_Number() OVER (Order By emprank)) AS Columns,
CONVERT(VARCHAR,emprank) as e
FROM Table1
) p
PIVOT
(
MAX (e)
FOR Columns IN
(
' + #cols + ' )
) as pvt
UNION
SELECT *
FROM
(
SELECT ''Column'' + CONVERT(VARCHAR,Row_Number() OVER (Order By emprank)) AS Columns,
CONVERT(VARCHAR,empexp) as e,
FROM Table1
) p
PIVOT
(
MAX (e)
FOR Columns IN
(
' + #cols + ' )
) as pvt
'
EXECUTE (#query)
The result of the above code is as shown below:
Column1 Column2 Column3 Column4 Column5
1 2 3 3 3
11 10 9 9 9
Joyce Angela Lily Sasha Harry
UNIVERSITY MASTERS MASTERS UNIVERSITY UNIVERSITY
Now, my application requires that I display each of the columns in this table separately, i.e. each of the columns, and not rows, of this table needs to be exported from this table and transferred, possibly into a temporary table, from which it can be displayed easily.
I am well aware of the fact that relational DBs are designed in such a way so as to consider rows, not columns, as individual entities. However, I am constrained by the application on which I am working, which requires code that extracts the data in this table column-wise so that they can be displayed separately.
How would I go about doing this?

This is a terminology overlap.
In SQL, "row" means (roughly) a single entity, and "column" means a property on the entities.
In UI, "row" means data arranged horizontally, and "column" means data arranged vertically.
Your requirement is that you should display your entities vertically. So, retrieve your entities (SQL rows) and then add code in your application to display this data in UI columns. It's unfortunate and perhaps confusing that the terms are the same here, but remember, your database structure (and choice of terminology) is completely irrelevant to your UI layout.
As to what code in your application is required to display the data... well, you haven't even told us what language it's in, so I can't help there.

Related

Calculate the row value based on different formula

I want to calculate the cell values based on the given formula in each row.
Input record is:
Expected ouput is:
First thing is your formula is not correct. As I understand there is unique row for each sl. And to do the sum of col1 and col2 correct formula is (col1 + col2). So correct that thing first.
After that you may implement this using dynamic sql as
create table tab_sum ( id int, col1 int, col2 int, col varchar(max) )
insert into tab_sum ( id, col1, col2, col )
values ( 1, 3, 5, '(col1 + col2)' )
, ( 2, 4, 6, '(col1 + col2)' )
DECLARE #query NVARCHAR(MAX) = '';
WITH CTE_DistinctFormulas AS
(
SELECT DISTINCT col as Formula FROM tab_sum
)
SELECT #query = #query + '
UNION ALL
SELECT id, col1, col2, col, ' + Formula + ' AS CalcValue FROM tab_sum WHERE col = ''' + Formula + ''' '
FROM CTE_DistinctFormulas;
SET #query = STUFF(#query,1,12,'');
PRINT #query;
EXEC (#query);
Or if editing formula is not an option for you then you may try this, But make sure you have only one row for each id and its corresponding values otherwise it will sum all of the similar rows in one.
create table tab_sum ( id int, col1 int, col2 int, col varchar(max) )
insert into tab_sum ( id, col1, col2, col )
values ( 1, 3, 5, 'sum(col1 + col2)' )
, ( 2, 4, 6, 'sum(col1 + col2)' )
DECLARE #query NVARCHAR(MAX) = '';
WITH CTE_DistinctFormulas AS
(
SELECT DISTINCT col as Formula FROM tab_sum
)
SELECT #query = #query + '
UNION ALL
SELECT id, col1, col2, col, ' + Formula + ' AS CalcValue FROM tab_sum WHERE col = ''' + Formula + ''' group by id, col1, col2, col '
FROM CTE_DistinctFormulas;
SET #query = STUFF(#query,1,12,'');
PRINT #query;
EXEC (#query);
I think you want something like this:
select *,case when row_cal like '%1%' and row_cal like '%2%' then value1 + value2
when row_cal like '%1%' and row_cal like '%3%' then value1 + value3
end as result from YourTable
Did you have a look at the case/ when expression? See MS doc: CASE
So your query would be somthing like:
SELECT sl, audience_1, audience_2, audience_3,
CASE WHEN audience_3 > 0 THEN audience_1 + audience_3
ELSE audience_1 + audience_2
END audience_total
FROM audience
;
You did not provide the criteria, so I had to guess :-)
(I did not run the query since I had no server handy :-) )
UPDATE after comment by OP:
If you have 71 different columns for audience in the same table you have a differnt issue imho.
Then my solution would be:
split out the audience number to a detail table with sl,
audience_type and audience_count
create a audience_calc_config table with two columns calc_method and audience_type
for each different calculation and audience type add an entry to the above
table
remove formula from original table and replace with appropriate
calc_method
Then run a simple select with a JOIN, GROUP BY sl and SUM(audience)
(Of course it would be even nicer to split the audience_calc to a
master/ detail, so you can have a foreign key...)

SQL Server Trace file create unique identifier for field TextData of type NTEXT

To analyse my imported trace file it would like to have a unique value for Select Distinct TextData from myImportedTraceFile
I tried using hashbyte although i am not sure if MD5 is the right tool to create a unique identifier. Even if this were the case (please tell me if it is so) then i still have the problem that
Using HASHBYTES('MD5', CAST(TextData AS varchar(7999))) As TextData_HashBytes cuts a few rows of (see this reply)
What can i do to create a unique identifier for every unique value (Select Distinct TextData from ..) in the column TextData?
Update
Based on the post from Dan i created this testcase
Drop Table #Temp
Create Table #Temp
(
A int,
B NText
)
Insert Into #Temp ( A, B)
Select 1, 'some space' UNION ALL
Select 2, ' some space' UNION ALL
Select 3, ' some space ' UNION ALL
Select 4, 'some space ' UNION ALL
Select 5, ' some space ' UNION ALL
Select 6, ' some space '
-- this returns 6 rows
Select
HASHBYTES('MD5', CAST(B AS nvarchar(MAX)))
, CAST(B AS nvarchar(MAX)) as B from #Temp;
-- this returns 3 rows
SELECT NEWID() AS UniqueID, B FROM
( Select DISTINCT CAST(B AS nvarchar(MAX)) AS B
FROM #Temp
) sq
These three rows are the result
' some space ' -- 2sp B + 1sp E --> row 5
' some space' -- 1sp B + 0sp E --> row 2
'some space ' -- 0sp B + 3sp E --> row 4
It is unclear how row 1 (0sp), 3 (1sp B+E) and 6 (2sp B+E) are handled.
So some whitespace is removed other not.
You could use a derived table with SELECT DISTINCT:
SELECT NEWID() AS UniqueID, TextData
FROM (
SELECT DISTINCT CAST(TextData AS nvarchar(MAX)) AS TextData
FROM myImportedTraceFile
) AS UniqueQueries;

SQL Pivot table without aggregate

I have a number of text files that are in a format similar to what is shown below.
ENTRY,1,000000,Widget 4000,1,,,2,,
FIELD,Type,A
FIELD,Component,Widget 4000
FIELD,Vendor,Acme
ENTRY,2,000000,PRODUCT XYZ,1,,,3,
FIELD,Type,B
FIELD,ItemAssembly,ABCD
FIELD,Component,Product XYZ - 123
FIELD,Description1,Product
FIELD,Description2,XYZ-123
FIELD,Description3,Alternate Part #440
FIELD,Vendor,Contoso
They have been imported into a table with VARCHAR(MAX) as the only field. Each ENTRY is a "new" item, and all the subsequent FIELD rows are properties of that item. The data next to the FIELD is the column name of the property. The data to the right of the property is the data I want to display.
The desired output would be:
ENTRY Type Component Vendor ItemAssembly Description1
1,000000,Widget 4000 A Widget 4000 Acme
2,000000,Product XYZ B Product XYZ-123 Contoso ABCD Product
I've got the column names using the code below (there are several tables that I have UNIONed together to list all the property names).
select #cols =
STUFF (
(select Distinct ', ' + QUOTENAME(ColName) from
(SELECT
SUBSTRING(ltrim(textFileData),CHARINDEX(',', textFileData, 1)+1,CHARINDEX(',', textFileData, CHARINDEX(',', textFileData, 1)+1)- CHARINDEX(',', textFileData, 1)-1) as ColName
FROM [MyDatabase].[dbo].[MyTextFile]
where
(LEFT(textFileData,7) LIKE #c)
UNION
....
) A
FOR XML PATH(''), TYPE).value('.','NVARCHAR(MAX)'),1,1,'')
Is a Pivot table the best way to do this? No aggregation is needed. Is there a better way to accomplish this? I want to list out data next to the FIELD name in a column format.
Thanks!
Here is the solution in SQL fiddle:
http://sqlfiddle.com/#!3/8f0b0/8
Prepare raw data in format (entry, field, value), use dynamic SQL to make pivot on unknown column count.
MAX() for string is enough to simulate "without aggregate" behavior in this case.
create table t(data varchar(max))
insert into t values('ENTRY,1,000000,Widget 4000,1,,,2,,')
insert into t values('FIELD,Type,A')
insert into t values('FIELD,Component,Widget 4000')
insert into t values('FIELD,Vendor,Acme ')
insert into t values('ENTRY,2,000000,PRODUCT XYZ,1,,,3,')
insert into t values('FIELD,Type,B')
insert into t values('FIELD,ItemAssembly,ABCD')
insert into t values('FIELD,Component,Product XYZ - 123')
insert into t values('FIELD,Description1,Product ')
insert into t values('FIELD,Description2,XYZ-123 ')
insert into t values('FIELD,Description3,Alternate Part #440')
insert into t values('FIELD,Vendor,Contoso');
create type preparedtype as table (entry varchar(max), field varchar(max), value varchar(max))
declare #prepared preparedtype
;with identified as
(
select
row_number ( ) over (order by (select 1)) as id,
substring(data, 1, charindex(',', data) - 1) as type,
substring(data, charindex(',', data) + 1, len(data)) as data
from t
)
, tree as
(
select
id,
(select max(id)
from identified
where type = 'ENTRY'
and id <= i.id) as parentid,
type,
data
from identified as i
)
, pivotsrc as
(
select
p.data as entry,
substring(c.data, 1, charindex(',', c.data) - 1) as field,
substring(c.data, charindex(',', c.data) + 1, len(c.data)) as value
from tree as p
inner join tree as c on c.parentid = p.id
where p.id = p.parentid
and c.parentid <> c.id
)
insert into #prepared
select * from pivotsrc
declare #dynamicPivotQuery as nvarchar(max)
declare #columnName as nvarchar(max)
select #columnName = ISNULL(#ColumnName + ',','')
+ QUOTENAME(field)
from (select distinct field from #prepared) AS fields
set #dynamicPivotQuery = N'select * from #prepared
pivot (max(value) for field in (' + #columnName + ')) as result'
exec sp_executesql #DynamicPivotQuery, N'#prepared preparedtype readonly', #prepared
Here your are, this comes back exactly as you need it. I love tricky SQL :-). This is a real ad-hoc singel-statement call.
DECLARE #tbl TABLE(OneCol VARCHAR(MAX));
INSERT INTO #tbl
VALUES('ENTRY,1,000000,Widget 4000,1,,,2,,')
,('FIELD,Type,A')
,('FIELD,Component,Widget 4000')
,('FIELD,Vendor,Acme ')
,('ENTRY,2,000000,PRODUCT XYZ,1,,,3,')
,('FIELD,Type,B')
,('FIELD,ItemAssembly,ABCD')
,('FIELD,Component,Product XYZ - 123')
,('FIELD,Description1,Product ')
,('FIELD,Description2,XYZ-123 ')
,('FIELD,Description3,Alternate Part #440')
,('FIELD,Vendor,Contoso');
WITH OneColumn AS
(
SELECT ROW_NUMBER() OVER(ORDER BY (SELECT 1)) AS inx
,CAST('<root><r>' + REPLACE(OneCol,',','</r><r>') + '</r></root>' AS XML) AS Split
FROM #tbl AS tbl
)
,AsParts AS
(
SELECT inx
,Each.part.value('/root[1]/r[1]','varchar(max)') AS Part1
,Each.part.value('/root[1]/r[2]','varchar(max)') AS Part2
,Each.part.value('/root[1]/r[3]','varchar(max)') AS Part3
,Each.part.value('/root[1]/r[4]','varchar(max)') AS Part4
,Each.part.value('/root[1]/r[5]','varchar(max)') AS Part5
FROM OneColumn
CROSS APPLY Split.nodes('/root') AS Each(part)
)
,TheEntries AS
(
SELECT DISTINCT *
FROM AsParts
WHERE Part1='ENTRY'
)
SELECT TheEntries.Part2 + ',' + TheEntries.Part3 + ',' + TheEntries.Part4 AS [ENTRY]
,MyFields.AsXML.value('(fields[1]/field[Part2="Type"])[1]/Part3[1]','varchar(max)') AS [Type]
,MyFields.AsXML.value('(fields[1]/field[Part2="Component"])[1]/Part3[1]','varchar(max)') AS Component
,MyFields.AsXML.value('(fields[1]/field[Part2="Vendor"])[1]/Part3[1]','varchar(max)') AS Vendor
,MyFields.AsXML.value('(fields[1]/field[Part2="ItemAssembly"])[1]/Part3[1]','varchar(max)') AS ItemAssembly
,MyFields.AsXML.value('(fields[1]/field[Part2="Description1"])[1]/Part3[1]','varchar(max)') AS Description1
FROM TheEntries
CROSS APPLY
(
SELECT *
FROM AsParts AS ap
WHERE ap.Part1='FIELD' AND ap.inx>TheEntries.inx
AND ap.inx < ISNULL((SELECT TOP 1 nextEntry.inx FROM TheEntries AS nextEntry WHERE nextEntry.inx>TheEntries.inx ORDER BY nextEntry.inx DESC),10000000)
ORDER BY ap.inx
FOR XML PATH('field'), ROOT('fields'),TYPE
) AS MyFields(AsXML)

SQL Pivot and Total ROLLUP

Please help me understand how to implement the ROLLUP on this pivot table.
I have been looking over several of the other written solutions to my requirement, but I seem to be missing something when I apply them to my situation. SQL is not my strength, but I feel like I understand more each time I accomplish something.
Creating this pivot statement below was a little struggle since I need to work with dynamic fields to summarize the total for different dID depending on the cID that the report is running.
I am using a method to dynamically create the NVARCHAR that will represent the columns I am applying SUM to in the pivot table. I've attempted to use the ROLLUP function as part of the GROUP BY condition of the SELECT without success.
The general layout I would like is something like this below. Additionally, I have a second pivot with the same dID from a different dataset that I would like to merge the Totals from both into a new table, and create a GrandTotal. I imagine this would be best accomplished with a UNION
dID, dName, qID's, dID_Total
1, A, 100-113, #
2, B, 100-113, #
3, C, 100-113, #
4, D, 100-113, #
5, E, 100-113, #
DECLARE #sID int = 100
DECLARE #cID int = 5
DECLARE #ColumnName AS NVARCHAR(MAX)
--Get distinct values of the PIVOT Column
SELECT #ColumnName = ISNULL(#ColumnName + ',', '') + QUOTENAME(qID)
FROM(SELECT qID FROM qTable WHERE cID = #cID) AS QuestionID
DECLARE# PivotTableSQL NVARCHAR(MAX)
SET# PivotTableSQL = N'
SELECT
dID, dName, ' + #ColumnName + '
FROM (
SELECT dID, dName, qID, weighted AS [score]
FROM sourceTable
WHERE sID = ' + CAST(#sID AS nvarchar(8)) + ' AND cID = ' + CAST(#cID AS nvarchar(8)) + '
) AS PivotData
PIVOT (
SUM(score)
FOR [qID] IN (
' + #ColumnName + '
)
) AS PivotTable
GROUP BY
dID, dName,' + #ColumnName
EXECUTE(#PivotTableSQL)

PIVOT in sql 2005

I need to pivot one column (Numbers column).
example need this data:
a 1
a 2
b 3
b 4
c 5
d 6
d 7
d 8
d 9
e 10
e 11
e 12
e 13
e 14
Look like this
a 1 2
b 3 4
c 5
d 6 7 8 9
e 10 11 12 13 14
any help would be greatly appreciated...
Using ROW_NUMBER(), PIVOT and some dynamic SQL (but no cursor necessary) :
CREATE TABLE [dbo].[stackoverflow_198716](
[code] [varchar](1) NOT NULL,
[number] [int] NOT NULL
) ON [PRIMARY]
DECLARE #sql AS varchar(max)
DECLARE #pivot_list AS varchar(max) -- Leave NULL for COALESCE technique
DECLARE #select_list AS varchar(max) -- Leave NULL for COALESCE technique
SELECT #pivot_list = COALESCE(#pivot_list + ', ', '') + '[' + CONVERT(varchar, PIVOT_CODE) + ']'
,#select_list = COALESCE(#select_list + ', ', '') + '[' + CONVERT(varchar, PIVOT_CODE) + '] AS [col_' + CONVERT(varchar, PIVOT_CODE) + ']'
FROM (
SELECT DISTINCT PIVOT_CODE
FROM (
SELECT code, number, ROW_NUMBER() OVER (PARTITION BY code ORDER BY number) AS PIVOT_CODE
FROM stackoverflow_198716
) AS rows
) AS PIVOT_CODES
SET #sql = '
;WITH p AS (
SELECT code, number, ROW_NUMBER() OVER (PARTITION BY code ORDER BY number) AS PIVOT_CODE
FROM stackoverflow_198716
)
SELECT code, ' + #select_list + '
FROM p
PIVOT (
MIN(number)
FOR PIVOT_CODE IN (
' + #pivot_list + '
)
) AS pvt
'
PRINT #sql
EXEC (#sql)
Just because I wanted to get some more experience with CTEs, I came up with the following:
WITH CTE(CTEstring, CTEids, CTElast_id)
AS
(
SELECT string, CAST(id AS VARCHAR(1000)), id
FROM dbo.Test_Pivot TP1
WHERE NOT EXISTS (SELECT * FROM dbo.Test_Pivot TP2 WHERE TP2.string = TP1.string AND TP2.id < TP1.id)
UNION ALL
SELECT CTEstring, CAST(CTEids + ' ' + CAST(TP.id AS VARCHAR) AS VARCHAR(1000)), TP.id
FROM dbo.Test_Pivot TP
INNER JOIN CTE ON
CTE.CTEstring = TP.string
WHERE
TP.id > CTE.CTElast_id AND
NOT EXISTS (SELECT * FROM dbo.Test_Pivot WHERE string = CTE.CTEstring AND id > CTE.CTElast_id AND id < TP.id)
)
SELECT
t1.CTEstring, t1.CTEids
FROM CTE t1
INNER JOIN (SELECT CTEstring, MAX(LEN(CTEids)) AS max_len_ids FROM CTE GROUP BY CTEstring) SQ ON SQ.CTEstring = t1.CTEstring AND SQ.max_len_ids = LEN(t1.CTEids)
ORDER BY CTEstring
GO
It might need some tweaking, but it worked with your example
The coalesce function could also be used here, similar to other questions that have been asked about concatenating data.
How to create a SQL Server function to "join" multiple rows from a subquery into a single delimited field?
This related question should have the answer you need: SQL Server: Examples of PIVOTing String data
A Matrix control in SSRS has dynamic columns, if this data is bound for a report anyways then you could use that. Otherwise you'll have to create a sql sproc that generates the sql like in the exaamples dynamicly and then executes it.
I'm not sure that what you're doing is really possible (or at least practical) in SQL - I'm not sure, because I'm still not exactly sure what you want to do.
You could build that pivot table in your client application, for example with:
select distinct Letter from MyTable
to get the list of letters, and then use a parameterized query inside a loop:
select Number from MyTable where Letter=:letter
to get the list of numbers for each letter.

Resources