Changing query based on multi-value parameter - tsql

I have a multi value parameter from a report that will hide or show columns accordingly. The dataset calls the corresponding stored procedure:
CREATE PROCEDURE RandomProc #Server NVARCHAR(100)
AS
SELECT
t1.Cluster,
t2.Server,
t2.ServerIP
FROM
Table1 t1
INNER JOIN Table2 t2
ON t1.ID = t2.ID
WHERE
t1.Cluster LIKE '%' + #Server + '%' OR
t2.Server LIKE '%' + #Server + '%' OR
t2.ServerIP LIKE '%' + #Server + '%'
If I were to untick Cluster from the report, in addition to the Cluster column being hidden from the report, t1.cluster should be removed from the SELECT and WHERE clause. And so on for other values.
Is this possible? Should this be done at the query level or through SSRS?
Writing multiple queries isn't feasible as there are too many options to pick from in the multi-value parameter.

I would create two additional parameters and add them to the query and SSRS to filter data set:
#Cluster NVARCHAR(100)
,#ServerIP NVARCHAR(100)
You could use dynamic SQL to add/remove columns in the SELECT; however, SSRS report will not be working properly if your columns disappear or appear.
Update:
Step by step how to setup conditional column visibility in SSRS:
http://bhushan.extreme-advice.com/conditionally-setting-column-visibility-in-ssrs/

Related

SQL Pivot using a subquery in FOR

Using SQL Server 2016 and referring to this article:
https://www.sqlshack.com/dynamic-pivot-tables-in-sql-server/
That article uses this pivot:
SELECT * FROM (
SELECT
[Student],
[Subject],
[Marks]
FROM Grades
) StudentResults
PIVOT (
SUM([Marks])
FOR [Subject]
IN (
[Mathematics],
[Science],
[Geography]
)
) AS PivotTable
How can you change the query so that the Subjects ([Mathematics], [Science], [Geography]) don't have to be hardcoded in the query?
Can you rather get the Subject list using a subquery? How do you get the FOR to work with a query like this?
...
FOR [Subject]
IN (
SELECT subject FROM grades WHERE student = "Jacob"
)
How can you change the query so that the Subjects ([Mathematics], [Science], [Geography]) don't have to be hardcoded in the query?
You can't; you'll have to form the SQL as a string and execute it dynamically
SQL makes it easy to have a variable number of columns (you just write more words in a SELECT), which then also makes it easy to forget that columns are like properties of an object (and an entire row is like an instance of an object); they aren't something that vary dynamically every time you run a program. As a Person you don't have a Name this week and not next week.
The number of columns output from a query isn't meant to vary; the number of rows is. If you want variable numbers of attributes, you'll have to form them as rows and then have your front end behave differently to account for them (i.e. don't do the pivot). If you can't do this because you have no front end, and you really do need a varying number of columns, you have to write a different SQL each time (which you can do by concatenating together a new SQL string and EXECing it, but be under no illusions - it works because it's a totally different SQL/the programmatic equivalent of you editing your hardcoded query and re-running it)
It looks something like (not tested - consider this pseudocode):
DECLARE #sql VARCHAR(4000) = CONCAT('
SELECT * FROM (
SELECT
[Student],
[Subject],
[Marks]
FROM Grades
) StudentResults
PIVOT (
SUM([Marks])
FOR [Subject]
IN (',
SELECT STRING_AGG(Subject, ',') FROM (SELECT DISTINCT QUOTENAME(Subject) FROM Grades) x,
' )
) AS PivotTable'
) --end concat
EXEC #sql

How to get list of constraints of a table along with their respective column names in SQL Server 2008 R2

I want to list all constraint names with their respective column name for a table. I am using the below mentioned code :
select *
from sys.objects
where parent_object_id = object_id('qw') --this gives me the constraints lists but does not give me columns in which they are applied.
select *
from sys.columns
where object_id = object_id('qw') -- this gives me the column list of the table.
My problem is that I am not able to join these two queries to get columns along with their constraints.
Sounds like you need CONSTRAINT_COLUMN_USAGE.

Returning the results in the 'SAME ORDER' as the input param

In the below query what should I do to get the results in the same order as the input param.
DECLARE #sql varchar(max)
SET #sql = 'SELECT a.num AS Num, a.photo as Photo , row_number() over (order by (select 0)) rn
FROM tbl a (nolock) WHERE a.num IN (' + #NumList + ') '
I pass in to the #NumList param the following (as an example):
1-235,1-892,2-847,1-479,3-890,1-239,2-892
This works fine, however I need the results returning in the 'SAME ORDER' as the input param.
I have created a SQL Fiddle
If #NumList contains unique values you could use CharIndex to find their position within the param ex:
order by charindex(a.num, #NumList)
Create a local temporary table #numbers. Ensure that it has an auto-increment identity column.
Insert the numbers from #NumList into #numbers in the correct order.
Split #NumList at the commas and turn the individual values into rows. See e.g. this question for ideas how to do this.
Alternatively, turn #NumList from a VARCHAR-typed variable into a table variable. (That way, you might even be able to use it directly, i.e. in place of #numbers.)
Modify your query so that rows from tbl a are joined to #numbers. Also, add an ORDER BY clause that sorts the result by the auto-increment identity column of #numbers.

Feasibility of recreating complex SQL query in Crystal Reports XI

I have about 10 fairly complex SQL queries on SQL Server 2008 - but the client wants to be able to run them from their internal network (as opposed to from the non-local web app) through Crystal Reports XI.
The client's internal network does not allow us to (a) have write access to their proprietary db, nor (b) allow us to set up an intermediary SQL server (meaning we can not set up stored procedures or other data cleaning).
The SQL contains multiple instances of row_number() over (partition by col1, col2), group by col1, col2 with cube|rollup, and/or (multiple) pivots.
Can this even be done? Everything I've read seems to indicate that this is only feasible via stored procedure and I would still need to pull the data from the proprietary db first.
Following is a stripped back version of one of the queries (eg, JOINs not directly related to functionality, WHERE clauses, and half a dozen columns have been removed)...
select sum(programID)
, sum([a.Asian]) as [Episodes - Asian], sum([b.Asian]) as [Eps w/ Next Svc - Asian], sum([c.Asian])/sum([b.Asian]) as [Avg Days to Next Svc - Asian]
, etc... (repeats for each ethnicity)
from (
select programID, 'a.' + ethnicity as ethnicityA, 'b.' + ethnicity as ethnicityB, 'c.' + ethnicity as ethnicityC
, count(*) as episodes, count(daysToNextService) as episodesWithNextService, sum(daysToNextService) as daysToNextService
from (
select programID, ethnicity, datediff(dateOfDischarge, nextDateOfService) as daysToNextService from (
select t1.userID, t1.programID, t1.ethnicity, t1.dateOfDischarge, t1.dateOfService, min(t2.dateOfService) as nextDateOfService
from TABLE1 as t1 left join TABLE1 as t2
on datediff(d, t1.dateOfService, t2.dateOfService) between 1 and 31 and t1.userID = t2.userID
group by t1.userID, t1.programID, t1.ethnicity, t1.dateOfDischarge, t1.dateOfService
) as a
) as a
group by programID
) as a
pivot (
max(episodes) for ethnicityA in ([A.Asian],[A.Black],[A.Hispanic],[A.Native American],[A.Native Hawaiian/ Pacific Isl.],[A.White],[A.Unknown])
) as pA
pivot (
max(episodesWithNextService) for ethnicityB in ([B.Asian],[B.Black],[B.Hispanic],[B.Native American],[B.Native Hawaiian/ Pacific Isl.],[B.White],[B.Unknown])
) as pB
pivot (
max(daysToNextService) for ethnicityC in ([C.Asian],[C.Black],[C.Hispanic],[C.Native American],[C.Native Hawaiian/ Pacific Isl.],[C.White],[C.Unknown])
) as pC
group by programID with rollup
Sooooooo.... can something like this even be translated into Crystal Reports XI?
Thanks!
When you create your report instead of selecting a table or stored procedure choose add command
This will allow you to put whatever valid TSQL statement in there that you want. Using Common Table Expressions (CTE's) and inline Views I've managed to create some rather large complex statements (excess of 400 lines) against Oracle and SQL Server so it is indeed feasible, however if you use parameters you should consider using sp_executesql you'll have to figure out how to avoid SQL injection.

Easy way to identify required fields in a table

Scenario: Table with over 100 fields (not my doing... I inherited this)
Only 50 these fields are required to be displayed on a web site
They want to maintain the other 50 fields for historical purposes.
There is a possibility that some of the not required fields may become required sometime in the future.
Problem: I'm looking for a way to easily indentify the 50 required fields such that I could pull the field names with a query.
Psuedo Query: Select FieldNames from TableName where Required = Yes
Is there a setting I could change?
What about using Extended Properties?
Thanks in advance for any direction you can provide.
Unless I'm missing a nuance to your question, use the INFORMATION_SCHEMA table for COLUMNS. This query identifies all the columns in table dbo.dummy that are required.
SELECT
IC.COLUMN_NAME
FROM
INFORMATION_SCHEMA.COLUMNS IC
WHERE
IC.TABLE_SCHEMA = 'dbo'
AND IC.TABLE_NAME = 'dummy'
AND IC.IS_NULLABLE = 'NO'
After doing more thinking, perhaps you wanted a generic query that would grab all the required columns and then build out the select query. This query covers that possible request
DECLARE
#hax varchar(max)
, #schemaName sysname
, #tableName sysname
SELECT
#schemaName = 'dbo'
, #tableName = 'dummy'
; WITH A AS
(
-- this query identifies all the columns that are not nullable
SELECT
IC.TABLE_SCHEMA + '.' + IC.TABLE_NAME AS tname
, IC.COLUMN_NAME
FROM
INFORMATION_SCHEMA.COLUMNS IC
WHERE
IC.TABLE_SCHEMA = #schemaName
AND IC.TABLE_NAME = #tableName
AND IC.IS_NULLABLE = 'NO'
)
, COLUMN_SELECT (column_list) AS
(
-- this query concatenates all the column names
-- returned by the above
SELECT STUFF((SELECT '], [' + A.Column_Name
FROM A
FOR XML PATH('')),1, 2, '')
)
-- Use the above to build a query string
SELECT DISTINCT
#hax = 'SELECT ' + CS.column_list + '] FROM ' + A.tname
FROM
A
CROSS APPLY
COLUMN_SELECT CS
-- invoke the query
EXECUTE (#hax)
How about creating a view that only has the required fields.
I am not sure if I understand the question correctly. Is this what you are looking for? The code is in MS SQL.
select t.name as TABLE_NAME, c.name as COLUMN_NAME, c.is_nullable
from sys.tables t
inner join sys.columns c on c.object_id = t.object_id
WHERE t.name = '<TableName>'
and c.is_nullable = 0
There's no flag you can put on a field to determine whether it's relevant or not -- that's what the SELECT list is for. A couple of ideas...
1) Split the historical data out into a separate table, with a one-to-one relationship to the source table.
2) Re-name the historical fields in your table as "OBSOLETE_" + fieldname. This will at least give you a quick visual reference for when you're writing your sql.
3) Create a view. Big drawback to this one would be that you can take some big performance hits as soon as you try to use the view as a table in other queries. But if you're just pulling off it directly without joining it, you should be fine.
We use separate metatables describing all tables and columns in database. We store information like friendly name (for example 'username' column shoud be displayed to user as 'User name'), formating, etc. You could use this approach to store information about required columns.
We have tried object extended properties (sp_addextendedproperty etc.), but metatable(s) solution came up better for us.
Within TSQL this is not easy as you cannot dynamically build the columns in the select line nor the alias name for those columns. The parser and query optimizer need some stuff to be static. Is it an ASP.NET web site? In your development environment (e.g. C#) you could dynamically build the query.