Dynamic pivot columns without dynamic SQL statement? - sql

I'm just wondering if there's any way to do a pivot query, with dynamic column names, without resorting to dynamic sql (declare #sql_text varchar(max) = 'select ...' etc.)
Dynamic SQL just rubs me the wrong way.
Basically I have a query like this (and I had to change all the table/column names to protect IP so if there's a syntax error somewhere don't worry about it)
declare #sec_class_ids table (CLASS_ID varchar(50));
insert #sec_class_ids (CLASS_ID) values
('987987987'), -- END USER - SAVE AND EXPORT [987987987]
('654654654'), -- END USER - SAVE [654654654]
('321321321') -- 'END USER - SPECIAL - SAVE AND EXPORT [321321321]'
select * from (
select
class.NAME as sec_class_name,
sec_attr.NAME as sec_attr,
'YES' as granted
from sec_class class
inner join class_sec_attr
on class.class_id = class_sec_attr.class_id
inner join sec_attr
on sec_attr.sec_attr_id = class_sec_attr.sec_attr_id
inner join #sec_class_ids input
on input.class_id = class.class_id
) as sec_attrs
pivot (
max(sec_attrs.granted)
--for sec_attrs.sec_class_id in (#sec_class_ids)
for sec_points.sec_class_name in ([END USER - SAVE AND EXPORT],[END USER - SAVE],[END USER - SPECIAL - SAVE AND EXPORT])
) as sec_class_comparison
;
I would like to be able to use the table var (shown in the comment) rather than manually setting the columns for each query. I am aware this is possible and quite easy with dynamic SQL, but I'd like to avoid doing that if possible in any way.

Unfortunately there is no way to do this without dynamic SQL. PIVOT requires that the values are known when the query is executed so if you have unknown names, then you have to use dynamic SQL.

I have never heard of a way to do this with a query, however I remember I was able to set this up in a report (SSRS) which was really neat. Depending on what you plan to do with the data this might work for you.

Related

INSERT FROM EXISTING SELECT without amending

With GDPR in the UK on the looming horizon and already have a team of 15 users creating spurious SELECT statements (in excess of 2,000) across 15 differing databases I need to be able to create a method to capture an already created SELECT statement and be able to assign surrogate keys/data WITHOUT rewriting every procedure we already have.
There will be a need to run the original team members script as normal and there will be requirements to pseudo the values.
My current thinking is to create a stored procedure along the lines of:
CREATE PROC Pseudo (#query NVARCHAR(MAX))
INSERT INTO #TEMP FROM #query
Do something with the data via a mapping table of real and surrogate/pseudo data.
UPDATE #TEMP
SET FNAME = (SELECT Pseudo_FNAME FROM PseudoTable PT WHERE #TEMP.FNAME = PT.FNAME)
SELECT * FROM #TEMP
So that team members can run their normal SELECT statements and get pseudo data simply by using:
EXEC Pseudo (SELECT FNAME FROM CUSTOMERS)
The problem I'm having is you can't use:
INSERT INTO #TEMP FROM #query
So I tried via CTE:
WITH TEMP AS (#query)
..but I can't use that either.
Surely there's a way of capturing the recordset from an existing select that I can pull into a table to amend it or capture the SELECT statement; without having to amend the original script. Please bear in mind that each SELECT statement will be unique so I can't write COLUMN or VALUES etc.
Does any anyone have any ideas or a working example(s) on how to best tackle this?
There are other lengthy methods I could externally do to carry this out but I'm trying to resolve this within SQL if possible.
So after a bit of deliberation I resolved it.
I passed the Original SELECT SQL to SP that used some SQL Injection, which when executed INSERTed data. I then Updated from that dataset.
The end result was "EXEC Pseudo(' Orginal SQL ;')
I will have to set some basic rules around certain columns for now as a short term fix..but at least users can create NonPseudo and Pseudo data as required without masses of reworking :)

Passing a multi-select input parameter as a dataset parameter with IN condition - SSRS

I have a report with an input parameter P_Region that allows the user to select 1-* options. I then need to pass the results of this parameter to a DataSet query. It works if i only select one option but not if i select two.
What i have:
The DataSet parameter for it is set to:
=join(Parameters!P_Region.Value, ",")
The query is:
WHERE ... AND D.City IN(#P_Region)
I have trieda few different things with no luck as well:
=Split(Parameters!Item.Value, ",")
=Split(Join(Parameters!#ReportParameter1.Value,","),",")
WHERE D.Region IN (SELECT * FROM dbo.split(#Department)) with no luck.
Any help is much appreciated!
Solution: (Sorry I feel like this is a pretty easy solution but I did not see it anywhere online and I'm super new with SSRS)
Right click on the dataset
Go to the Parameters tab and set the query parameter back to the default value (No Joins or Splits)
Go to the Filters tab and add the expression of the field you want to be in the parameter, choose the operator "In" and set the value as [#<'Parameter Name'>']
Note: Understand that the other records will still be returned by the query but they will just be filtered. If you are returning large sets of data and using very little of it, this may not be the best route to go.
I think you where on the right track with WHERE D.Region IN (SELECT * FROM dbo.split(#Department)) with no luck.
I use a tabled valued function to create a filter table and parse the values there.
WHERE D.Region IN (SELECT ID FROM dbo.CreateIntIDTable(#Department))
ALTER FUNCTION [dbo].[CreateIntIDTable](#IDList NVARCHAR(3000))
RETURNS #T TABLE (ID INT)
AS BEGIN
WHILE(CHARINDEX(',',#IDList)>0)BEGIN
INSERT INTO #T
SELECT LTRIM(RTRIM(SUBSTRING(#IDList,1,CHARINDEX(',',#IDList)-1)))
SET #IDList = SUBSTRING(#IDList,CHARINDEX(',',#IDList)+LEN(','),LEN(#IDList))
END
INSERT INTO #T SELECT LTRIM(RTRIM(#IDList))
RETURN
END
Try to Create separate dataset for your selection dropdown then you can use your where condition.
for ex.
1) Data set (SELECT Company FROM Companymaster ORDER BY Company)
2) where
company in (#CompanyName)
Did you set up the parameter properties to accept multiple values ?
you don't need to do any Join or Split function if properties ar set up properly.

Jet engine (Access) : Passing a list of values to a stored procedure

I am currently writing a VBA-based Excel add-in that's heavily based on a Jet database backend (I use the Office 2003 suite -- the problem would be the same with a more recent version of Office anyway).
During the initialization of my app, I create stored procedures that are defined in a text file. Those procedures are called by my app when needed.
Let me take a simple example to describe my issue: suppose that my app allows end-users to select the identifiers of orders for which they'd like details. Here's the table definition:
Table tblOrders: OrderID LONG, OrderDate DATE, (other fields)
The end-user may select one or more OrderIDs, displayed in a form - s/he just has to tick the checkbox of the relevant OrderIDs for which s/he'd like details (OrderDate, etc).
Because I don't know in advance how many OrderID s/he will select, I could dynamically create the SQL query in the VBA code by cascading WHERE clauses based on the choices made on the form:
SELECT * FROM tblOrders WHERE OrderID = 1 OR OrderID = 2 OR OrderID = 3
or, much simpler, by using the IN keyword:
SELECT * FROM tblOrders WHERE OrderID IN (1,2,3)
Now if I turn this simple query into a stored procedure so that I can dynamically pass list of OrderIDs I want to be displayed, how should I do? I already tried things like:
CREATE PROCEDURE spTest (#OrderList varchar) AS
SELECT * FROM tblOrders WHERE OrderID IN (#OrderList)
But this does not work (I was expecting that), because #OrderList is interpreted as a string (e.g. "1,2,3") and not as a list of long values. (I adapted from code found here: Passing a list/array to SQL Server stored procedure)
I'd like to avoid dealing with this issue via pure VBA code (i.e. dynamically assigning list of values to a query that is hardcoded in my application) as much as possible. I'd understand if ever this is not possible.
Any clue?
You can create the query-statement string dynamically. In SQL Server you can have a function whose return value is a TABLE, and invoke that function inline as if it were a table. Or in JET you could also create a kludge -- a temporary table (or persistent table that serves the function of a temporary table) that contains the values in your in-list, one per row, and join on that table. The query would thus be a two-step process: 1) populate temp table with INLIST values, then 2) execute the query joining on the temp table.
MYTEMPTABLE
autoincrementing id
QueryID [some value to identify the current query, perhaps a GUID]
myvalue one of the values in your in-list, string
select * from foo
inner join MYTEMPTABLE on foo.column = MYTEMPTABLE.myvalue and MYTEMPTABLE.QueryId = ?
[cannot recall if JET allows ANDs in INNER JOIN as SQL Server does --
if not, adjust syntax accordingly]
instead of
select * from foo where foo.column IN (... )
In this way you could have the same table handle multiple queries concurrently, because each query would have a unique identifier. You could delete the in-list rows after you're finished with them:
DELETE FROM MYTEMPTABLE where QueryID = ?
P.S. There would be several ways of handling data type issues for the join. You could cast the string value in MYTEMPTABLE as required, or you could have multiple columns in MYTEMPTABLE of varying datatypes, inserting into and joining on the correct column:
MYTEMPTABLE
id
queryid
mytextvalue
myintvalue
mymoneyvalue
etc

SQL Server 2008 column select

In SQL Server, if I got a table with like 20 columns and I want 18 of them, can I say something like * minus columnname1, columnname2, course right now I write them all.
But if you could it would be much easier.
Little hint to replace the asterisk with column names in SQL Management Studio in no time without any fancy plugin:
Select your written query (no matter how many joins, etc.)
Right click and select "Design Query in Editor..."
Simply click "Ok"
The asterisk should have been expanded to column names now :)
Ofc it's possible to select/deselect any column in the query editor..
Hth
It is not possible. However if you are using SQL Server Management Studio 2008 / 2005 you can right click on the table and select the "Script Table as > SELECT To" menu option. This will save you typing the column names, or purchase Red-Gate's SQL Prompt
Out of the box - no, it's not possible. You have to spell out all the columns you want explicitly.
With SQL Server Management Studio 2008, there is intellisense which can help you select columns from a table - so that's certainly one step to help ease the pain.
Add-in tools like SQL Prompt offer more help - in SQL Prompt, you can type
SELECT *
FROM dbo.YourTable
and if you have the cursor just after the asterisk symbol (*), you can press <TAB> and expand the asterisk into the list of all columns for that table (and then remove the two you don't want) - or you can popup a window and pick those columns you really want.
Very handy, very useful, very much speeding up development - but it's not a free tool.....
You can use select TOP (18) * from givenTable is you want 18 rows.
There is no such method for columns. In fact column names are stored in master db and you can extract them and consruct query looking like what you are asking for BUT it would not be easier than just select field1,field2 ... field18 from blaBlaBla.
SELECT table_name=sysobjects.name,
column_name=syscolumns.name,
datatype=systypes.name,
length=syscolumns.length
FROM sysobjects
JOIN syscolumns ON sysobjects.id = syscolumns.id
JOIN systypes ON syscolumns.xtype=systypes.xtype
WHERE sysobjects.xtype='U'
and sysobjects.name='myTableName'
ORDER BY sysobjects.name,syscolumns.colid
will give you the list of your columns. You can write select generator based on this query.
I'd like to add to the answer of, "No, it's not possible directly in SQL". I would love to have that feature too! It sucks when you're trying to do some quick debugging on a 10+ column table that has a varbinary(max).
But I really just want to point out an alternative to Kane's tip for SSMS 2008 (Sql Server Management Studio).
If you open the Object Explorer (right-click in the query window and choose "Open Server in Object Explorer"), navigate to the node for the table in question. Expand the node so you can see the "Columns" node. Now "drag" the Columns node over to your query window and "drop" it. It will paste in all the column names for the table--and you can use it directly in a SELECT clause.
It is not possible as far as I know.
I have created a script for easy copy/pasting multiple columns, you might find it useful. See:
http://www.sqlservercentral.com/scripts/102375/
The script is explained in detail there, but in short for those who do not have an account on sqlservercentral:
It's a stored procedure that i can run using a shortcut. Type in your tablename (also works with temp tables and views), highlight it, hit the shortcut and it will display the columns of the table. From there you can easily copy multiple columns (the columns are also shown with a comma in front of the column name, so that also saves you some typing) and paste it in your query screen.
CREATE PROCEDURE [dbo].[sp_ColumnSelect]
#FullObjectName varchar(200)
AS
/*
Author: Robin van Schaik
Version: 1.3 (03-OCT-2012)
*/
DECLARE #Object varchar(200)
DECLARE #Schema varchar(200)
DECLARE #Database varchar(200)
DECLARE #IsTempTable bit
-- Break down parameter in Database/Schema/Object
SET #Object = PARSENAME(#FullObjectName,1)
SET #Schema = ISNULL(PARSENAME(#FullObjectName,2),'dbo')
SET #IsTempTable = case when left(#Object,1)='#' then 1 else 0 end
SET #Database = case when #IsTempTable=1 then 'tempdb' else PARSENAME(#FullObjectName,3) end
EXEC(
'SELECT
b.Name as ColumnStart
, '',''+b.Name as ColumnNext
, ''[''+b.Name+'']'' as ColumnStartBr
, '',[''+b.Name+'']'' as ColumnNextBr
FROM
' +#Database+'.sys.objects a
INNER JOIN
' +#Database+'.sys.columns b
ON a.object_id=b.object_id
INNER JOIN
' +#Database+'.sys.schemas d
ON a.schema_id=d.schema_id
WHERE
a.Object_ID=OBJECT_ID('''+#Database+'.'+#Schema+'.'+#Object+''')
AND d.name = '''+#Schema+'''
'
)

T-SQL, Cursors, FETCH INTO. How to use SELECT *

I am building a one off query to iterate through a set of joined tables. The select statement is using "SELECT *". Since it's is a table with lots of fields, I don't want to specify each column as a variable. Is there a way to FETCH INTO an array or SET of some sort and just grab the values I want?
Apparently not:
INTO #variable_name[ ,...n]
"Allows data from the columns of a fetch to be placed into local variables.
Each variable in the list, from left to right, is associated with the corresponding column in the cursor result set. The data type of each variable must either match or be a supported implicit conversion of the data type of the corresponding result set column. The number of variables must match the number of columns in the cursor select list."
If you are looking to use cursors you may find better flexibility with CLR Stored procedures
Even if there was, you wouldn't want to. Fetching extra fields is one of the most common causes of performance degredation in a SQL Server application. By doing so, you restrict the optimizer's ability to use indexes effectively.
How would you propose to "grab the values that you want" without specifying column names?
Why do you need to use a cursor? Those are frowned upon in SQL Server scenarios - most of the time, they're unnecessary, and usually they're very slow and hurt performance.
What are you trying to accomplish with your cursor? Couldn't you do it in a set-based manner and use SQL Server's abilities to the max??
Marc
In 2k5 (SQL 2000 has different system objects), use the SQL metadata to quickly write queries with long lists of table/column names:
SELECT
O.Name + '__' + C.Name + ' = ' + O.Name + '.' + C.Name + ','
FROM Sys.Objects O
JOIN Sys.Columns C
ON C.Object_Id = O.Object_Id
WHERE O.Name IN ('Table1', 'Table1')
ORDER BY O.Name, C.Column_Id
I'm not sure exactly what you're trying to do with the result set, but this might be a decent start.
Also, you could declare variables as SQL_VARIANT and FETCH result sets into them like this:
DECLARE #Col1 SQL_VARIANT
SELECT #Col1 = Table1.Column1 FROM Table1
PRINT CONVERT(varchar(max), #Col1)
I'm not sure what that gains you though.
This is being suggested only for those that work with SQL daily, not specifically to answer this question.
The RedGate product SQL Prompt has the ability to expand wild-card characters into individual field names by using short-cut keys (Ctrl-B, Ctrl-W). This can save you alot of time and make it easier to remove a few fields from the query for those occasions where you do need most of them.
In SQL2005 you could also right-click the table an choose open table. Then you click the little SQL-Icon and you see the select statement, something like select * from YourTable. Clicking again on the !-icon the select * from will be expanded to the full fieldlist.
Good luck.