How do I select two different years to display in query? - tsql

I need to be able to write a report in Report Builder with the ability to select two years to compare side by side.
I currently have my query setup to display the year defined in the parameter:
DECLARE # YR_CDE VARCHAR(4) = '2016'
**Select/From statement here**
WHERE YR_CDE = #YR_CDE
But this doesn't allow me to select multiple years when I throw the query into Report Builder. Does anyone know how I can fix this?

DECLARE #yr1 VARCHAR(4) = '2016',
#yr2 VARCHAR(4) = '2017'
SELECT <your-fields>
FROM <your-table>
WHERE yr_cde = #yr1
OR yr_cde = #yr2
If you are attempting to do a range of years, the following would work:
DECLARE #yr1 VARCHAR(4) = '2016',
#yr2 VARCHAR(4) = '2018'
SELECT <your-fields>
FROM <your-table>
WHERE yr_cde BETWEEN #yr1 AND #yr2

You can use IN in where clause as below:
Where YR_CDE in (#YR_CDE1, #YR_CDE2)

Related

Unable to use variable in DATEADD(#pinterval, #pinterval, #DayPlus)

DECLARE #pinterval INT = 1, #DayPlus DATETIME = '2016-07-01', #datepart VARCHAR(20) = 'MONTH'
SET #DayPlus = DATEADD(#datepart, #pinterval, #DayPlus)
SELECT #DayPlus
Do we have any alternative to accomplish task ? I have to do it in loop so I can't define it every time based on interval value. Only date part is not acceptable as variable because if I use as
DATEADD(MONTH, #pinterval, #DayPlus)
then it's working fine. Not sure but I can understand the issue but I am seeking for the quick solution. I have visited the web but didn't get exact solution.
It's not possible to substitute a variable for the first argument to DATEADD1, since what is wanted here is a name, not a string.
About the best you can do is a CASE expression:
SET #DayPlus =
CASE #datepart
WHEN 'MONTH' THEN DATEADD(MONTH, #pinterval, #DayPlus)
WHEN 'YEAR' THEN DATEADD(YEAR, #pinterval, #DayPlus)
WHEN 'DAY' THEN DATEADD(DAY, #pinterval, #DayPlus)
--TODO - add all parts you might wish to use
END
1This is even stated in the documentation:
User-defined variable equivalents are not valid.
if this is only the way then I have to repeat this for more than 10times
No, use CROSS APPLY
#Shnugo it sounds good, can you please help me by placing the complete code as an answer. Please !!
About CROSS APPLY: generate something like a variable dynamically
CREATE TABLE dbo.Test(ID INT, SomeDate DATE);
GO
INSERT INTO dbo.Test VALUES(1,{d'2017-01-01'}),(2,{d'2017-02-02'})
GO
DECLARE #Intervall VARCHAR(100)='DAY';
DECLARE #Count INT=1
SELECT dbo.Test.*
,t.AddedDate
FROM dbo.Test
CROSS APPLY(SELECT CASE #Intervall WHEN 'MONTH' THEN DATEADD(MONTH,#Count,SomeDate)
WHEN 'DAY' THEN DATEADD(DAY,#Count,SomeDate)
ELSE SomeDate END AS AddedDate) AS t;
GO
DROP TABLE dbo.Test;
You could use dynamic SQL:
DECLARE #pinterval INT = 1,
#DayPlus DATETIME = '2016-07-01',
#datepart NVARCHAR(20) = 'MONTH'
DECLARE #sql NVARCHAR(MAX) = N'SET #DayPlus = DATEADD('+#datepart+', #pinterval, #DayPlus)',
#params NVARCHAR(MAX) = N'#DayPlus DATETIME OUTPUT, #pinterval INT'
EXEC sp_executesql #sql, #params, #pinterval = #pinterval, #DayPlus = #DayPlus OUTPUT
SELECT #DayPlus
sp_executesql is used for 2 reasons:
to minimize SQL injections risk, it will not eliminate it because of #datepart
it will make sure your dynamic query's plan is going to be cached - which might be beneficial with simple queries (where query time vs compilation time matters)

Having a crystal report parameter as a declared SQL Value

I'm assuming this is a simple question, but I don't know Crystal Reports very well. I made a SQL Query which uses the declared dates fields of #beginning_date and #ending_date and I want Crystal to prompt for those fields when run. I added the paramter field in crystal and named it the same thing, but I'm unsure how to get them to sync up. My code is below. Thank you in advance.
--/*
DECLARE #beginning_date char(20)
DECLARE #ending_date char(20)
--SELECT #ending_date = '07/31/2016' --23:59:59'
--SELECT #beginning_date = '07/01/2016' --00:00:01'
--*/
SELECT --Sum(CASE when Billing_Ledger.subtype in ('BI','ND') then
--Billing_Ledger.amount ELSE 0 END) as 'Charges'
Patient_Clin_Tran.Clinic_id, Patient_Clin_Tran.service_id, Patient_Clin_Tran.program_id, Patient_Clin_Tran.protocol_id, billing_ledger.amount
FROM Billing_Ledger
JOIN Patient_Clin_Tran ON
Billing_Ledger.clinical_transaction_no = Patient_Clin_Tran.clinical_transaction_no
JOIN Coverage_Plan ON
Billing_Ledger.coverage_plan_id = Coverage_Plan.coverage_plan_id
and Billing_Ledger.hosp_status_code = Coverage_Plan.hosp_status_code
JOIN Payor ON
Coverage_Plan.payor_id = Payor.payor_id
WHERE ( Coverage_Plan.billing_type <> 'CAP' or Coverage_Plan.billing_type is null )
and Billing_Ledger.accounting_date >= #beginning_date
and Billing_Ledger.accounting_date < dateadd(day, 1, #ending_date)
and Patient_Clin_Tran.Clinic_id = 'NP' and payor.name = 'Mainecare' and (Billing_Ledger.subtype in ('BI','ND'))
--GROUP BY Patient_Clin_Tran.Clinic_id, Patient_Clin_Tran.service_id, Patient_Clin_Tran.program_id, Patient_Clin_Tran.protocol_id --, Payor.Name, billing_ledger.amount, payor.type
ORDER BY Patient_Clin_Tran.Clinic_id, Patient_Clin_Tran.service_id, Patient_Clin_Tran.program_id, Patient_Clin_Tran.protocol_id
Firstly you should turn your query into a stored procedure thus:
CREATE PROCEDURE [dbo].[mySPName]
#beginning_date date,
#ending_date date
AS
SELECT Patient_Clin_Tran.Clinic_id, Patient_Clin_Tran.service_id, Patient_Clin_Tran.program_id, Patient_Clin_Tran.protocol_id, billing_ledger.amount
etc.
Now when adding the database connection to your CR report file, after selecting your server and database you will see three options tables, views and stored procedures. Simply select the new procedure from the list. CR will now automtically add the parameters, and will prompt you for values. If you leave the values blank, then at runtime CR will automatically prompt the user for these values.
You will also note that I changed the type of the parameters to date; this is necessary so that CR knows to include a calendar selector in the parameter prompt.

Report Server - Can't declare parameters

My problem is that I was not able to declare parameters through Report Builder.
I was receiving the following error:
ERROR [42000] [Microsoft][ODBC SQL Server Driver][SQL Server]Must
declare the scalar variable "#param".
So I googled it and found that it can be solved easily by putting symbol "?" instead of "#parameter" and it did solved my problem for a while.
But now I have another problem. I have a select like:
select * from table t where t.date = ? or t.date2 = ? or t.date3 = ?
Where all three "?" are '2013-aug-01', but each "?" creates a new parameter in the parameters section of Report Builder.
How can I use one parameter for all three cases?
Try this
select * from table t where ? IN (t.date, t.date2, t.date3)
Another way of solving is just delete the two variables created by SSRS and update your query to
select * from table t where t.date = #param1 or t.date2 = #param1 or t.date3 = #param1
Another method write your query
select * from table t where t.date = ? or t.date2 = ? or t.date3 = ?
Let it create the 3 variables. Then go to Dataset, under dataset properties goto parameters section. There update the parameter value of 2 & 3 to Parameter1. Next go in the parameters section and delete the automatically generated Parameter2 & Parameter3.

Metadata about a column in SQL Server 2008 R2?

I'm trying to figure out a way to store metadata about a column without repeating myself.
I'm currently working on a generic dimension loading SSIS package that will handle all my dimensions. It currently does :
Create a temporary table identical to the given table name in parameters (this is a generic stored procedure that receive the table name as parameter, and then do : select top 0 * into ##[INSERT ORIGINAL TABLE NAME HERE] from [INSERT ORIGINAL TABLE NAME HERE]).
==> Here we insert custom code for this particular dimension that will first query the data from a datasource and get my delta, then transform the data and finally loads it into my temporary table.
Merge the temporary table into my original table with a T-SQL MERGE, taking care of type1 and type2 fields accordingly.
My problem right now is that I have to maintain a table with all the fields in it to store a metadata to tell my scripts if this particular field is type1 or type2... this is nonsense, I can get the same data (minus type1/type2) from sys.columns/sys.types.
I was ultimately thinking about renaming my fields to include their type in it, such as :
FirstName_T2, LastName_T2, Sex_T1 (well, I know this can be type2, let's not fall into that debate here).
What do you guyz would do with that? My solution (using a table with that metadata) is currently in place and working, but it's obvious that repeating myself from the systables to a custom table is nonsense, just for a simple type1/type2 info.
UPDATE: I also thought about creating user defined types like varchar => t1_varchar, t2_varchar, etc. This sounds like something a bit sluggy too...
Everything you need should already be in INFORMATION_SCHEMA.COLUMNS
I can't follow your thinking of not using provided tables/views...
Edit: As scarpacci mentioned, this somewhat portable if needed.
I know this is bad, but I will post an answer to my own question... Thanks to GBN for the help tho!
I am now storing "flags" in the "description" field of my columns. I, for example, can store a flag this way : "TYPE_2_DATA".
Then, I use this query to get the flag back for each and every column :
select columns.name as [column_name]
,types.name as [type_name]
,extended_properties.value as [column_flags]
from sys.columns
inner join sys.types
on columns.system_type_id = types.system_type_id
left join sys.extended_properties
on extended_properties.major_id = columns.object_id
and extended_properties.minor_id = columns.column_id
and extended_properties.name = 'MS_Description'
where object_id = ( select id from sys.sysobjects where name = 'DimDivision' )
and is_identity = 0
order by column_id
Now I can store metadata about columns without having to create a separate table. I use what's already in place and I don't repeat myself. I'm not sure this is the best possible solution yet, but it works and is far better than duplicating information.
In the future, I will be able to use this field to store more metadata, where as : "TYPE_2_DATA|ANOTHER_FLAG|ETC|OH BOY!".
UPDATE :
I now store the information in separate extended properties. You can manage extended properties using sp_addextendedproperty and sp_updateextendedproperty stored procedures. I have created a simple store procedure that help me to update those values regardless if they currently exist or not :
create procedure [dbo].[UpdateSCDType]
#tablename nvarchar(50),
#fieldname nvarchar(50),
#scdtype char(1),
#dbschema nvarchar(25) = 'dbo'
as
begin
declare #already_exists int;
if ( #scdtype = '1' or #scdtype = '2' )
begin
select #already_exists = count(1)
from sys.columns
inner join sys.extended_properties
on extended_properties.major_id = columns.object_id
and extended_properties.minor_id = columns.column_id
and extended_properties.name = 'ScdType'
where object_id = (select sysobjects.id from sys.sysobjects where sysobjects.name = #tablename)
and columns.name = #fieldname
if ( #already_exists = 0 )
begin
exec sys.sp_addextendedproperty
#name = N'Scd_Type',
#value = #scdtype,
#level0type = N'SCHEMA',
#level0name = #dbschema,
#level1type = N'TABLE',
#level1name = #tablename,
#level2type = N'COLUMN',
#level2name = #fieldname
end
else
begin
exec sys.sp_updateextendedproperty
#name = N'Scd_Type',
#value = #scdtype,
#level0type = N'SCHEMA',
#level0name = #dbschema,
#level1type = N'TABLE',
#level1name = #tablename,
#level2type = N'COLUMN',
#level2name = #fieldname
end
end
end
Thanks again

How to return text for default language

I have so tables:
and so data at Language table:
and so data at Text table:
I have to return text for requested language if it exists and text for default language if it does not exist. Is it possible to do that in one query (no while, please)?
Code:
DECLARE #CommentId bigint = 1
--DECLARE #LanguageCode nvarchar(2) = 'en' -- "english text" returns
DECLARE #LanguageCode nvarchar(2) = 'ua' -- nothing at this moment
SELECT
t.CommentId
,t.TextId
,t.[Text]
,t.LanguageId
,RequestedLanguageId = #LanguageCode
FROM dbo.common_Text t
INNER JOIN dbo.common_LanguageType l
ON t.LanguageId = l.LanguageId
WHERE l.Code = #LanguageCode
AND t.CommentId = #CommentId
Thank you.
I assume that something is messed up in the data you supplied. Didn't you mean to show a row in the text table with LanguageId = 2? Without using a recursive query or loop, you can't keep following the DefaultId of the language until you end up at English. Assuming there is a row in the text table for ukrainian's backup (2 = russian):
DECLARE
#CommentId BIGINT = 1,
#LanguageCode NVARCHAR(2) = 'ua';
SELECT
CommentId = COALESCE(t.CommentId, a.CommentId),
TextId = COALESCE(t.TextId, a.TextId),
[Text] = COALESCE(t.[Text], a.[Text]),
LanguageId = COALESCE(t.LanguageId, a.LanguageId),
RequestedLanguageId = #LanguageCode
FROM
dbo.common_LanguageType AS l
LEFT OUTER JOIN
dbo.common_Text AS t
ON l.LanguageId = t.LanguageId
AND t.CommentID = #CommentId
LEFT OUTER JOIN
dbo.common_Text AS a -- a for "alternate"
ON l.DefaultId = a.LanguageId
WHERE
l.Code = #LanguageCode
AND a.CommentID = #CommentId;
If this is not the case, you need to make the question more clear. If you have LanguageId 4, 'central ukrainian' with a DefualtId = 3, when that language is requested is the query supposed to check the text table for 4, when it's not found, it checks 4's default (3), when that's not found, it checks 3's default (2), when that's not found, it checks 2's default (1) and finally returns the row for 1? If this is the case you will certainly need a more complicated query (using either a recursive CTE or a loop).
Also for the language code you should probably use NCHAR(2) as opposed to NVARCHAR(2). I hope the column is not nullable and unique.
Solution was found on Database Administrators site