Struggling with getting results (Views, Variables, StoredProcedures) - sql

As I understand it, you cannot create a view with variables. The dilemma is I have to do some datediff on values. Our third party reporting software can only do basic Selects on Tables or views. I cannot call up any stored procedures or set variables there.
My original source data looks select * from tblquotestatuschangelog
results in this I would like to see these results
Quotenumber UpdatedOn Status UpdatedBy
----------------------------------------------
100001 04102019 Open domain/user
100001 04132019 Closed domain/user
I have done a pivot on this data to get the results in the desired format with this query. (There are more status types than in this example, and more can be added via the CRM we use) (Q1)
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX);
select #cols = STUFF((SELECT distinct ',' + QUOTENAME(T1.STATUS)
FROM tblCglQuoteStatusChangeLog as T1
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = 'SELECT quotenumber, ' + #cols + ' from
(
select quotenumber, status, updatedon, updatedby
from tblcglquotestatuschangelog
where updatedon > 2019-04-01
) x
pivot
(
max(updatedon)
for status in (' + #cols + ')
) p '
execute #query
To get these results
Quotenumber Open Closed
----------------------------------------------
100001 04102019 04132019
I would like to call (Q1) as a View to perform
Select
QuoteNumber,
case when datediff(day,Open, Closed) = 0 then Cast('SAMEDAY' AS NVARCHAR(20)) else cast(datediff(day,openedon,updatedon) as NVarCHAR(20)) end as TotalAge,
datediff(day,Open,SentToCustomer) as Stage1,
datediff(day,SentToCustomer,) as Stage2,
From V1
Any help or alternate direction to achieve results would be much appreciated.

You can use conditional aggregation to achieve your results - as long as you want to rely on the assumptions about the shape of your data. Those are your answers to my questions - that the tuple is unique and that there are only 2 status values. If you have more status values (and it smells like you do) you can extend the logic
use tempdb;
set nocount on;
go
if object_id('v1') is not null
drop view v1;
if object_id('chglog') is not null
drop table chglog;
go
create table chglog (IdNumber int not null, UpdatedOn date not null, Status varchar(10) not null, UpdatedBy varchar(20) not null
constraint chg_unique unique (IdNumber, Status),
constraint chg_check check (Status in ('Open', 'Closed'))
);
insert chglog(IdNumber, UpdatedOn, Status, UpdatedBy)
values (100001, '20190410', 'Open', 'domain/user'), (100001, '20190413', 'Closed', 'domain/user'),
(9999, '20190401', 'Open', 'zork'),
(99001, '20190402', 'Open', 'bob'),
(99001, '20190402', 'Closed', 'alice')
;
go
-- setup complete, now create a view to do a hard-coded pivot
create view v1 as
select IdNumber,
max(case Status when 'Open' then UpdatedOn else null end) as 'Open',
max(case Status when 'Closed' then UpdatedOn else null end) as 'Closed'
from chglog
group by IdNumber;
go
-- test the view
select * from v1
order by IdNumber;
-- now the query that you wanted to write/use
Select
IdNumber,
case when datediff(day, [Open], Closed) = 0 then N'SAMEDAY' else cast(datediff(day, [Open], Closed) as nvarchar(20)) end as TotalAge
-- datediff(day,Open,SentToCustomer) as Stage1,
-- datediff(day,SentToCustomer,) as Stage2,
from v1
order by IdNumber;
I'll point out that your "table" contained IdNumber but your last query referenced QuoteNumber (as well as other columns you did not mention). I just ignored some and guesssed some. This is just old-style pivoting based on hard-coded values. So long as you know ahead of time the values you need to consider when pivoting and you know that the domain is unchanging (or don't care about other values), this will what you asked.
The question you should consider is whether you should care about other status values that are added via your CRM after you create this view. But you're kinda stuck with the 3rd party reporting software anyways. A reporting tool that can't use anything but a table or view as a source of data seems primitive (to say the least).

Related

Data Selection SQL

I have a scenario where I need to select data based on month-end.
Raw data looks like:
ID
Date
Cost
IS_REVERSED
Reverse_ID
1
2021-01-01
$1
No
NULL
2
2021-01-30
$2
YES
NULL
3
2021-02-01
$3
NULL
2
4
2021-02-03
$4
No
NULL
Please note the IS_REVERSED flag column and Reverse_ID column. If the transaction is successful in the first attempt the flag is NO, But if the transaction is successful in the second attempt the flag is NULL
My desired output if I run the report for January end it should bring all transactions that happened in Jan (even if it reversed is Yes but reversal has not yet happened)
ID 1, 2
For next month-end, I need to report data from Jan and Feb combined. and the desired output should be
ID 1, 3 and 4
Id 2 should not be reported in because that has a reverse flag of Yes
Any pointers to achieve this would be much appreciated.
Create table Test_Report
(ID Int
,[Date] date
,Cost varchar(100)
,Is_reversed varchar(100)
,Reversed_ID int)
insert into Test_Report values
(1 ,'2021-01-01', '$1' , 'No', NULL),
(2 ,'2021-01-30', '$2' , 'YES', NULL),
(3 ,'2021-02-01', '$3' , NULL, 2),
(4 ,'2021-02-03', '$4' , 'No', NULL)
I need a single query where i can pass [date] as a condition to filter out records. (date < '2021-01-31' should bring id 1,2 )
(date <'2021-02-28' should bring id 1,3,4) ID 2 should not come as the transaction is reversed and we have a new transaction (Id 3) with IS_REVERSE flag as NULL.
Thanks
I used a dynamic SQL query to achieve the results. Since your data is not clear and I see the datatypes are not correct, assumptions are used. So, DML and DDL are included with the answer
Create table Test_Report
(ID Int
,[Date] varchar(100)
,Cost varchar(100)
,Is_reversed varchar(100)
,Reversed_ID int)
insert into Test_Report values (1 ,'01 Jan', '$1' , 'No', NULL)
,(1 ,'30 Jan', '$2' , 'YES', NULL)
,(1 ,'01 Feb', '$3' , NULL, 2)
,(1 ,'03 Feb', '$4' , 'No', NULL)
declare #from varchar(100) = 'Jan'
, #to varchar(100) = 'Feb'
declare #where varchar(max)
declare #query varchar(max)
set #query = 'select * from Test_Report where '
if #from = #to
begin
set #where = ' [Date] like ''%'+#from+ '%'' and ( isnull(Is_reversed,'''') in (''No'', ''YES'', '''')) and Reversed_ID is null'
end
else
begin
set #where = ' ( [Date] like ''%'+#from+ '%'' or [Date] like ''%'+#to+ '%'' ) and ( isnull(Is_reversed,'''') in (''No'', ''''))'
end
Declare #Main varchar(max) = #query + #where
--This statement can be used to test the resulted query
--select #Main
exec(#main)
I am assuming you need to impute the date of the report you wish to run. In this case I would declare a variable for your WHERE clause.
This variable outputs the current date, but you can replace the GETDATE() syntax with a date value to make the query dynamic. Now the WHERE clause...
DECLARE #DATEVAR AS DATE = GETDATE()
SELECT *
,EOMONTH(#Test_Report.[Date],0) [EOMONTH for WHERE clause]
FROM #Test_Report
WHERE (Is_reversed != 'YES'
OR Is_reversed IS NULL)
AND #Test_Report.[Date] <= EOMONTH(#DATEVAR,0)
The EOMONTH() function can be used here to evaluate the end of the month selected, and select all records less than that month-end date. Since you want both records where Is_reversed is not YES and is NULL, we need to put an OR clause in parenthesis.
Desired Output:
A more clever way to deal with the nulls is the ISNULL(,) function. You can use it in your WHERE clause to make that constraint a 1-liner.
SELECT *
,EOMONTH(#Test_Report.[Date],0) [EOMONTH for WHERE clause]
FROM #Test_Report
WHERE ISNULL(Is_reversed,'No') != 'YES'
AND #Test_Report.[Date] <= EOMONTH(#DATEVAR,0)
Either method should do the trick!

Use column if it exists, another if doesn't in SQL Server

I have a number of SQL Server databases (different versions from 2012 to 2019). The schema in each one is very similar but not exactly the same. For example, there's table ORDERS, which has about 50 columns - and one column is called differently in two different databases:
in DB1: select p_user from orders
in DB2: select userpk from orders
Note that I showed two databases above, but there are actually more than 20 - some are DB1 type, the others are DB2 type
I can't do much about these differences - they are historic - and changing the schema to match is not an option.
I want to be able to run the same SQL statement against all of these databases at once. I'd like to write the query in such a way that it would use one column if it exists and another if it doesn't. For example:
select
case
when COL_LENGTH('orders', 'p_user') IS NOT NULL
then
orders.p_user
else
orders.userpk
end
from orders
This unfortunately doesn't work, as SQL server seems to try to evaluate both results regardless of whether the condition is true or false. The same thing happens if I use IIF function.
If I simply run
select
case
when COL_LENGTH('orders', 'p_user') IS NOT NULL
then
'orders.p_user'
else
'orders.userpk'
end
then I do get the correct string, which means my condition is correct.
How can I formulate the SQL statement to use one or the other column based on whether the first one exists?
If you can't change anything then your best (and maybe only) option is to use dynamic SQL. A query will only compile if all parts can be resolved at compile time (before anything runs) - which is why e.g. this will not compile:
IF COL_LENGTH('orders', 'p_user') IS NOT NULL THEN
select p_user from orders
ELSE
select userpk as p_user from orders
END
But this will work:
DECLARE #SQL NVARCHAR(MAX)
IF COL_LENGTH('orders', 'p_user') IS NOT NULL THEN
SET #SQL = 'select p_user from orders'
ELSE
SET #SQL = 'select userpk as p_user from orders'
END
EXEC (#SQL)
Fix your tables by adding a computed column:
alter table db1..orders
add statuspk as (p_status);
(Or choose the other name.)
Then, your queries will just work without adding unnecessary complication to queries.
create table orders1(colA int, colB int, colABC int);
insert into orders1 values(1, 2, 3);
go
create table orders2(colA int, colB int, colKLM int);
insert into orders2 values(5, 6, 7);
go
create table orders3(colA int, colB int, colXYZ int);
insert into orders3 values(10, 11, 12);
go
select colA, colB, vcolname as [ABC_KLM_XYZ]
from
(
select *,
(select o.* for xml path(''), elements, type).query('
/*[local-name() = ("colABC", "colKLM", "colXYZ")][1]
').value('.', 'int') as vcolname
from orders1 as o
) as src;
select colA, colB, vcolname as [ABC_KLM_XYZ]
from
(
select *,
(select o.* for xml path(''), elements, type).query('
/*[local-name() = ("colABC", "colKLM", "colXYZ")][1]
').value('.', 'int') as vcolname
from orders2 as o
) as src;
select colA, colB, vcolname as [ABC_KLM_XYZ]
from
(
select *,
(select o.* for xml path(''), elements, type).query('
/*[local-name() = ("colABC", "colKLM", "colXYZ")][1]
').value('.', 'int') as vcolname
from orders3 as o
) as src;
go
drop table orders1
drop table orders2
drop table orders3
go
I ended up using dynamic sql like so:
declare #query nvarchar(1000)
set #query = concat(
'select count(distinct ', (case when COL_LENGTH('orders', 'p_user') IS NOT NULL then 'orders.p_user' else 'orders.userpk' end), ')
from orders'
);
execute sp_executesql #query
This solved my immediate issue.

SQL Server improve query performance #Temp Table, Bulk insert

I am working on an old auditing stored procedure that is taking a long time to get data causing timeouts on the system. We have managed to get the time down from over 20 minutes to running just over a minute, this is still too long.
I am running on SQL Server 2008 R2.
My question is is there anything I could do to improve the speed of the query? In particular the temporary tables and the a bulk insert statement.
SELECT
dbo.[Audit Result Entry Detail].PK_ID,
+ every other value in the table (all values required)
INTO
#temp5
FROM
dbo.[Audit Result Entry Detail]
An INNER JOIN then occurs on dbo.[Audit Register] another select occurs and added to another temporary table #result is created.
Result temporary table gets the old and new result values for comparison. Below I have provided what event occurs on #temp5. Note these are just snippets; the stored procedure is way too big to post everything.
SELECT
RED.[FK_RegisterID],
total of a 106 rows are selected here :(
FROM
#temp5 AS RED
LEFT JOIN
#temp5 AS OLD ON RED.IdentityColumn = OLD.IdentityColumn
LEFT JOIN
[Audit Register] AS REG ON REG.PK_ID = RED.FK_RegisterID
SELECT MAX(PK_ID)
OVER (PARTITION BY FK_RegisterID) AS MAX_PK_ID
FROM #temp5
DROP TABLE #temp5
The second part of my question is a bulk insert into this table created at the very top of the stored procedure
DECLARE #AuditOverView TABLE
(
FK_RegisterID INT,
Audit_Date DATETIME,
ContextUser NVARCHAR(30),
Original_Value NVARCHAR(255),
New_Value NVARCHAR(255),
Assay NVARCHAR(200),
Inst NVARCHAR(40),
LotLevel NVARCHAR(3),
Lot_ExpiryDate NVARCHAR(10),
Lot_Number NCHAR(50),
Audit_Type NVARCHAR(10),
Partnumber INT,
[Type] NVARCHAR(50),
SubType NVARCHAR(50)
)
The insert statement below:
INSERT INTO #AuditOverView
SELECT DISTINCT
t.FK_RegisterID,
t.Audit_Date,
t.ContextUser,
CONVERT(NVARCHAR, Original_Date_Run, 127) AS Original_Value,
CONVERT(NVARCHAR, New_Date_Run, 127) AS New_Value,
t.Assay AS 'Assay',
Instrument AS 'Inst',
'1' AS 'LotLevel',
Lot1_Expiry_Date AS 'Lot_ExpiryDate',
Lot1Number AS 'Lot_Number',
t.Audit_Type,
part_number AS Partnumber,
'QC Result' AS [Type],
'Result Date' AS SubType
FROM
#Result AS t
INNER JOIN
(SELECT MAX(Audit_Date) AS DATE, FK_RegisterID
FROM #Result
GROUP BY FK_RegisterID) dt ON t.FK_RegisterID = dt.FK_RegisterID
AND t.Audit_Date = dt.DATE
WHERE
RTRIM(Lot1Number) != ''
AND (CONVERT(NVARCHAR, Original_Date_Run, 120) != CONVERT(NVARCHAR, New_Date_Run, 120)
OR t.Audit_Type = 'i'
OR t.Audit_Type = 'd')
This insert statement occurs about 50 times and the only line changes occur are:
'Result Date' AS SubType
CONVERT(NVARCHAR, Original_Date_Run, 120) != CONVERT(NVARCHAR, New_Date_Run, 120)
I can provide more information if needed. Any help would be much appreciated to improve performance.

Adding 1 number to TOP Select hangs the query

OK, first a disclaimer. I'm using an Entity Attribute Value approach in a couple of my tables. So basically I have a List of attributes in a single column in one table that I want to then populate it in a single row in a seperate view.
I found this solution and it works great:
SQL: Dynamic view with column names based on column values in source table
However the initial load was extremely slow (it took over 27 minutes to populate 514 rows). I thought something didn't seem right at all so I messed around with selecting portions of the Client table using TOP. I got instant results. I found that I could instantly queue the entire database this way. However I found a very weird caveat. The most I could select was 5250 records.
Up to this point I was still getting instant results. If I tried to select 5251, the query hangs. I tried it on a test server and got the same limitations but with a different number (I could select a max of 5321 there). Keep in mind the table has only 514 records, so I have no idea why adding 1 number to a TOP select would cause it to hang. Does anyone have any input in this? Here's my working sql query below:
DECLARE #cols AS NVARCHAR(MAX)
DECLARE #query AS NVARCHAR(MAX)
select #cols = STUFF((SELECT distinct ',' + QUOTENAME(a.AttributeName)
from AttributeCodes a
FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = 'SELECT TOP 5250 ClientID, ' + #cols + ' from
(
select c.ClientID
, c.[Value]
, a.AttributeName
from Client c
inner join AttributeCodes a
on a.AttributeCodeId = c.AttributeCodeID
)x
pivot
(
min([Value])
for AttributeName in (' + #cols + ')
)p'
execute(#query)
EDIT:
OK it seems as though the problem is that the execution plan is completely changed by adding another digit. I'll post the two results below. I still don't know why it would change, and if there is any way I can prevent it from using a Hash Match instead of an Inner Join.
Execution Plan 1 (instant):
Execution Plan 2 (30+ minutes):
Without looking at the exact index design and knowing exactly the size and scope of what you're selecting and what's in the database, I can't give you the exact reason.
But what I can tell you is that there are cost thresholds that SQL Server uses to come up with plans about whether it's more advantageous to scan a table or perform a large amount of seeks. What's happening here is that SQL Server seems to be crossing that threshold at the 5250/5251 boundary.
If you were to add many more data rows to your main table, rebuild the statistics, and then re-query, you would likely find that SQL Server would stop scanning and hashing and would return to the lookup-based plan because the proportion of rows it would have to repeatedly seek would once again be lower.
So, how to eliminate this problem? I'll start out by saying that EAV can be fine for certain types of designs in certain scenarios, but when you're pulling a lot of data from the system, or you want reports off of these models, EAV becomes problematic, and these are the types of problems you tend to see. Since you're selective
You have a couple of possible solutions here.
Add a LOOP JOIN hint to your join. Yuck.
Be more selective in what you're asking from the database -- for example, just ask it for the values for a single client at a time.
Re-evaluate your reasons for using the EAV model and redesign your data model using conventional techniques.
Again, I don't know much about your reasoning for this design, so if I were in your position and my back were against the wall, I would probably do a combination of 1) and 3). I'd add the hint to "staunch the bleeding" as it were, and then I'd consider re-evaluating the decision to use an entity-attribute-value design altogether.
The following is a formatted comment, not an answer.
Out of morbid curiosity, I've been fiddling about with the following test code. I realize that is does not have the proper code for checking for the existence of the test tables, that the edge cases for the random numbers are undoubtedly wrong, ... . So it goes.
The intent is to create considerably more test data and larger results than those described in the question. With the parameters shown it requires roughly 306 seconds to initialize and 87 seconds to run the dynamic query on my notebook. (Windows 7 Professional 64-bit, 16GB memory, SQL Server 2008 R2 64-bit.) I've seen no indications of any difficulties. Daniel, do you see any obvious differences e.g. a different data type for the Value column that might be larger? Version of SQL Server? Available memory? Did I completely muck up your EAV representation?
-- Test parameters.
declare #NumberOfAttributes as Int = 1000
declare #NumberOfClients as Int = 1000
declare #NumberOfSampleRows as Int = 1000000
declare #NumberOfTopRows as Int = 10000
declare #Start as DateTime = GetDate()
-- Houseclean any prior data.
if Object_Id( 'AttributeCodes' ) is not NULL
drop table AttributeCodes
if Object_Id( 'Client' ) is not NULL
drop table Client
-- Create the tables.
create table AttributeCodes (
AttributeCodeId Int Identity(1,1) not NULL,
AttributeName VarChar(64) not NULL )
create table Client (
ClientId Int not NULL,
AttributeCodeId Int not NULL,
[Value] VarChar(64) not NULL )
set nocount on
-- Generate some sample attributes.
declare #Count as Int
set #Count = #NumberOfAttributes
while ( #Count > 0 )
begin
insert into AttributeCodes ( AttributeName ) values
( 'Attr_' + Right( '000' + Cast( #Count as VarChar(8) ), 4 ) )
set #Count = #Count - 1
end
-- Generate some sample client data.
declare #ClientId as Int
declare #AttributeCodeId as Int
set #Count = #NumberOfSampleRows
while ( #Count > 0 )
begin
set #ClientId = 1 + Cast( Rand() * #NumberOfClients as Int )
set #AttributeCodeId = 1 + Cast( Rand() * #NumberOfAttributes as Int )
insert into Client ( ClientId, AttributeCodeId, Value ) values
( #ClientId, #AttributeCodeId, Replicate( 'i', Cast( Rand() * 64 as Int ) ) )
set #Count = #Count - 1
end
-- Build the list of columns.
declare #Cols as NVarChar(Max)
select #Cols = Stuff(
( select ',' + QuoteName( AttributeName ) from AttributeCodes order by AttributeName for XML path(''), type).value( '.[1]', 'NVarChar(max)' ), 1, 1, '' );
-- Build an execute the summary query.
declare #Query as NVarChar(Max)
set #Query = 'select top (' + Cast( #NumberOfTopRows as VarChar(8) ) + ') ClientId, ' + #Cols +
' from ( select C.ClientId, C.[Value], A.AttributeName from Client as C inner join AttributeCodes as A on A.AttributeCodeId = C.AttributeCodeId ) as X' +
' pivot ( Min( [Value] ) for AttributeName in (' + #cols + ') ) as P'
declare #InitializationComplete as DateTime = GetDate()
execute( #Query )
select DateDiff( ms, #Start, #InitializationComplete ) as 'Initialization (ms)',
DateDiff( ms, #InitializationComplete, GetDate() ) as 'Query (ms)',
DateDiff( mi, #Start, GetDate() ) as 'Total (min)'

How to use PIVOT in SQL Server 2005 Stored Procedure Joining Two Views

Good Morning,
I have 2 views: ICCUDays which contains one record per account with fields ACCOUNT and ICCUDays, ICCUEnctrSelectedRevCatsDirCost which contains multiple records per account with fields ACCOUNT, UBCATEGORY, and DirectCost.
My Goal: To create a stored procedure that outputs one record per ACCOUNT with ICCUDays and DirectCost by UBCATEGORY. This will be a crosstab or pivot and has to allow for the possibility of nulls in one or more direct cost ubcategory bucket. Finally, this crosstab or pivot needs to be sent to a new table EnctrUBCatPivot.
Questions: What is the correct PIVOT syntax for the above scenario? Given that I want to ouptut direct cost for however many UBCATEGORY entries, how do I write the TSQL to iterate over these and pivot by account and UBCATEGORY? Is all this accomplished in one sproc, or does it have to be separated into multiple sprocs to write the results out to a table?
Here's the code I've written so far:
ALTER PROCEDURE [dbo].[spICCUMain]
-- Add the parameters for the stored procedure here
AS
declare #columns varchar(8000)
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
-- Insert statements for procedure here
SELECT #columns = COALESCE(#columns + ',[' + cast(UBCATEGORYmid as varchar) + ']','[' + cast(UBCATEGORYmid as varchar)+ ']')
FROM vwICCUEnctrSelectedRevCatsDirCost
GROUP BY UBCATEGORYmid
DECLARE #query VARCHAR(8000)
SET #query = '
SELECT *
FROM vwICCUEnctrSelectedRevCatsDirCost
PIVOT
(
MAX(DirectCost)
FOR [UBCATEGORYmid]
IN (' + #columns + ')
)
AS p'
EXECUTE(#query)
END
This works fine in that it outputs Account and all the Direct Costs for each UBCATEGORY. However, I need to inner join to vwICCUDAYS on ACCOUNT to add a column to the pivot for ICCUDays. Final pivot columns should be Account, ICCUDays, Direct Cost for each UBCATEGORYmid.
I'm not very familiar with the coalesce syntax and thus cannot discern how to modify it to add further columns, nor am I sure how/where to add the inner join syntax to add ICCUDays.
Can someone point me in the proper direction?
Thanks,
Sid
You need to know all of the possible values to PIVOT by. So it is difficult to do this with T-SQL directly unless you use dynamic SQL and this can get hairy pretty quickly. Probably better to pass all of the rows back to the presentation tier or report writer and let it turn them sideways.
Here is a quick PIVOT example if you know all of the UBCategory values in advance. I left out ICCUDays since it seems rather irrelevant unless there are columns that come from that view as part of the result.
USE tempdb;
GO
SET NOCOUNT ON;
GO
-- who on earth is responsible for your naming scheme?
CREATE TABLE dbo.ICCUEnctrSelectedRevCatsDirCost
(
Account INT,
UBCategory VARCHAR(10),
DirectCost DECIMAL(9,2)
);
INSERT dbo.ICCUEnctrSelectedRevCatsDirCost
SELECT 1, 'foo', 5.25
UNION SELECT 1, 'bar', 6.25
UNION SELECT 1, 'smudge', 8.50
UNION SELECT 2, 'foo', 9.25
UNION SELECT 2, 'brap', 2.75;
SELECT Account,[foo],[bar],[smudge],[brap] FROM
dbo.ICCUEnctrSelectedRevCatsDirCost
-- WHERE <something>, I assume ???
PIVOT
(
MAX(DirectCost)
FOR UBCategory IN ([foo],[bar],[smudge],[brap])
) AS p;
GO
DROP TABLE dbo.ICCUEnctrSelectedRevCatsDirCost;
To make this more dynamic, you'd have to get the comma separated list of DISTINCT UBCategory values, and build the pivot on the fly. So it might look like this:
USE tempdb;
GO
SET NOCOUNT ON;
GO
-- who on earth is responsible for your naming scheme?
CREATE TABLE dbo.ICCUEnctrSelectedRevCatsDirCost
(
Account INT,
UBCategory VARCHAR(10),
DirectCost DECIMAL(9,2)
);
INSERT dbo.ICCUEnctrSelectedRevCatsDirCost
SELECT 1, 'foo', 5.25
UNION SELECT 1, 'bar', 6.25
UNION SELECT 1, 'smudge', 8.50
UNION SELECT 2, 'foo', 9.25
UNION SELECT 2, 'brap', 2.75
UNION SELECT 3, 'bingo', 4.00;
DECLARE #sql NVARCHAR(MAX),
#col NVARCHAR(MAX);
SELECT #col = COALESCE(#col, '') + QUOTENAME(UBCategory) + ','
FROM
(
SELECT DISTINCT UBCategory
FROM dbo.ICCUEnctrSelectedRevCatsDirCost
) AS x;
SET #col = LEFT(#col, LEN(#col)-1);
SET #sql = N'SELECT Account, $col$ FROM
dbo.ICCUEnctrSelectedRevCatsDirCost
-- WHERE <something>, I assume ???
PIVOT
(
MAX(DirectCost)
FOR UBCategory IN ($col$)
) AS p;';
SET #sql = REPLACE(#sql, '$col$', #col);
--EXEC sp_executeSQL #sql;
PRINT #sql;
GO
DROP TABLE dbo.ICCUEnctrSelectedRevCatsDirCost;
Then to "send the data to a new table" you can just make the query an INSERT INTO ... SELECT instead of a straight SELECT. Of course, this seems kind of useless, because in order to write that insert statement, you need to know the order of the columns (which isn't guaranteed with this approach) and you need to have already put in columns for each potential UBCategory value anyway, so this seems very chicken and egg.