query multiple tables with columns based on multiple conditions - tsql

I have three columns: crew info, training info, position info. I am trying to create a report that shows the most recent date of certain trainings that each crew member has taken. Each training should have its own column but some trainings are only required for certain positions.
I have tried a case statement statement as well as just filtering in a where statement but keep getting errors. Some of the research did says that t SQL doesn't have an if statement but some says it does.
Here is the Access code that works: Ballast Water Management:
Max(IIf([CURRENT_FLAG]=1 And [QUALIFICATION_ID] In (14800000086) And [CMC_POS_CODE_TRANSLATOR] In (1,3,6,8,12,13,15,18,20,25,32,90,91,92,94,96,98,100,102,103,230),[ISSUED_ON],IIf([CMC_POS_CODE_TRANSLATOR] Not In (1,3,6,8,12,13,15,18,20,25,32,90,91,92,94,96,98,100,102,103,230),"","0")))
What I want is to either show the latest date that a crew member completed the training (if required), a blank if it's not required for them, or a 0 if it's required and they don't have it.

Okay, one of the problems you have is that you are essentially returning three data types:
a date (possibly even a datetime);
a BIT (0/1);
an empty string (VARCHAR).
So you need some way to handle all three. I assumed that you would want:
a date in YYYYMMDD format as a string;
the 0 is a string of "0";
the empty string is already a string.
So your query becomes this:
SELECT
MAX(
CASE
WHEN [CURRENT_FLAG] = 1
AND [QUALIFICATION_ID] = 14800000086
AND [CMC_POS_CODE_TRANSLATOR] IN (1,3,6,8,12,13,15,18,20,25,32,90,91,92,94,96,98,100,102,103,230)
THEN CONVERT(VARCHAR(8), [ISSUED_ON], 112)
ELSE
CASE
WHEN [CMC_POS_CODE_TRANSLATOR] NOT IN (1,3,6,8,12,13,15,18,20,25,32,90,91,92,94,96,98,100,102,103,230)
THEN ''
ELSE '0'
END
END)
FROM
MY_TABLE;
If this doesn't work what might help is showing us what you tried, and the actual error you got?
Also, TSQL does indeed allow use of IF statements, but not inside a SELECT query. So you could do this:
IF #my_dog_has_no_nose = 1
BEGIN
SELECT 'how does he smell?' AS question;
ELSE
SELECT NULL AS question;
END;
But in a SELECT statement you will have to use CASE to allow redirection.

Related

AND/OR Statement in Tableau

Does anyone know how to transcribe this in Tableau:
WHERE DATE(created) = '2021-01-01'
AND (source = 'T' OR (a.promo = 'TK' AND source != 'T'))
Basically, I'm validating if source equals to "T" and grabbing all those results, but sometimes a promo but does not get flagged under the "T" source.
Is there a way to have filter that validates under this nested WHERE clause?
Thanks in advance!
If you're wanting to use a calculated field in the place of SQL then:
IF DATE([created]) = '2021-01-01'
AND ([source] = 'T' OR ([promo] = 'TK' AND [source] <> 'T'))
THEN [measure or dimension] END
That's assuming that the fields in Tableau end up being given those names.
EDIT
The calculated field above works by just ignoring data that doesn’t meet the conditions, which is very flexible — effectively pulling the condition from the WHERE clause into the SELECT clause of the query that Tableau generates.
In some other cases, you really do want to first filter to a set of data and then calculate on the resulting rows — either for performance or logic reasons.
In that case, you can define a boolean calculated field with just the condition, put it on the Filter shelf, and choose to filter to records whether the calculated field evaluates to True.
Both approaches are useful.

Azure Data Factory : returns an array of dates from a specified range

I'm trying to returns an array of dates in data factory. But i just want the user to specify a date range with two parameters, startDate and endDate :
I want to return this array by specifying "12-08-2020" and "12-13-2020" in trigger :
["12-08-2020","12-09-2020","12-10-2020","12-12-2020","12-13-2020"]
Do did not find a simple way to do it yet.
One way i thought about would be :
add a lookup activity on a date dimension,
then add two filters to select only items greater than startDate and lower than endDate.
But this seems to be cumbersome and overkill. Is there a simpler way to do it ?
EDIT :
This answer seems to be relevant (i did not see it at first) : Execute azure data factory foreach activity with start date and end date
I think we can use recursive query in Lookup activity.
The pseudo code is as follows:
In sql we can use this query to get a table:
;with temp as
(
select CONVERT(varchar(100),'12-08-2020', 110) as dt
union all
select CONVERT(varchar(100), DATEADD(day,1,dt), 110) from temp
where datediff(day,CONVERT(varchar(100), DATEADD(day,1,dt), 110),'12-13-2020')>=0
) select * from temp
The result is as follows:
So in ADF, I think we can use a Lookup sql query to return the result what you want.
According to this official document, we only need to replace the parameters of the sql statement.
Next,I will use '#{pipeline().parameters.startDate}' to return a date string, note: There is a pair of single quotes outside.
I set two parameters as follows:
Type the following code into a Lookup activity.
;with temp as
(
select CONVERT(varchar(100),'#{pipeline().parameters.startDate}', 110) as dt
union all
select CONVERT(varchar(100), DATEADD(day,1,dt), 110) from temp
where datediff(day,CONVERT(varchar(100), DATEADD(day,1,dt), 110),'#{pipeline().parameters.endDate}')>=0
) select * from temp
Don't select First row only.
The debug result is as follows:
I had similar use case and ended up using Until with little changes.
Two parameters which takes two parameters start_day and end_day
Also have to introduce two variables for implementing counter logic. more details can be found at how-to-increment-parameter-data-factory
and finally the expression in the untill block is
#less(int(adddays(pipeline().parameters.end_day, 0, 'yyyyMMdd')), int(adddays(pipeline().parameters.start_day, int(variables('counter')), 'yyyyMMdd')))
final note the until block executes when the expression returns false and loops out on true
I managed to get something similar to work with a combination of derived column transformation using a mapLoop() function followed by a flatten transformation.
The derived column expression first calculates an array of dates in a single column.
mapLoop(toInteger((To_Date - From_Date)/86400000)),toDate(addDays(From_Date,#index)))
where 86400000 is the number of miliseconds in 24 hours
The flatten transformation uses this column to unroll the array into separate rows.

SSRS Grouping Summary - with Max not working

This is the data that comes back from the database
Data Sample for one season (the report returns values for two):
What you can see is groupings, by Season, Theater then Performance number and lastly we have the revenue and ticket columns.
The SSRS Report Has three levels of groupings. Pkg (another ID that groups the below), venue -- the venue column and perf_desc -- the description column linked tot he perf_no.
Looks like this --
What I need to do is take the revenue column (a unique value) for each Performance and return it in a separate column -- so i use this formula.
sum(Max(Fields!perf_tix.Value, "perf_desc"))
This works great, gives me the total unique value for each performance -- and sums them up by the pkg level.
The catch is when i need to pull the data out by season.
I created a separate column looks like this
it's yellow because it's invisible and is referenced elsewhere. But the expression is if the Season value = to the Parameter (passed season value) -- then basically pull the sum of each of the tix values and sum them up. This also works great on the lower line - the line where the grouping exists for pkg -- light blue in my case.
=iif(Fields!season.Value = Parameters!season.Value, Sum(Max(Fields!perf_tix.Value, "perf_desc")), 0)
However, the line above -- the parent/header line its giving me the sum of the two seasons values. Basically adding it all up. This is not what I want and also why is it doing this. The season value is not equal to the passed parameter for the second season value so why is it adding it to the grouped value.
How do I fix this??
Since your aggregate function is inside your IIF function, only the first record in your dataset is being evaluated. If the first one matches the parameter, all records would be included.
This might work:
=IIF(Fields!season.Value = Parameters!season.Value, Sum(Max(Fields!perf_tix.Value, "perf_desc")), 0)
It might be better if your report was also grouping on the Venue, otherwise you count may include all values.

Parameter to find all records or exclude NULL

I have found several articles on how to accomplish the reverse of what I want to do with several methods (IS NULL, CASE, COALESCE), but I think at this point I am more confused than ever after reading all this and probably making solution harder than it needs to be. I am new to T-SQL and I am currently using VS 2005 to build a basic medical report.
I have the Date Range parameter working properly by using a convert command to ignore time stamp giving me all records for the day or date range. I am now wanting to filter SSRS reprot by perliminary report date to find records with preliminary report, or, all records in table.
The database has NULL if no preliminary report was created
The database has time stamp if preliminary report was created. (showing date and time it was created)
I need to find all records not NULL, or all records. (using a parameter)
I have a parameter "Display Prelim Reports only?" #PrelimOnly with a YES or NO answer.
If I use the following it will show all records correctly (all records not NULL showing only records with Prelim report/time stamp present)
LIS_Results.Prelim_Report_Date <> '#PrelimOnly' ----User selects YES it passes NULL
however, if user selects NO, how would I get it to display all records including NULL?
Thank you for any help
Thank you both for your help, it was ultimately a combination of both that got it going. Syntax is as follows.
WHERE (#PrelimOnly = 'NO') AND (CONVERT(VARCHAR(10), LIS_Results.Final_Report_Date, 101) BETWEEN #ReportStartDate AND #ReportEndDate) OR (LIS_Results.Prelim_Report_Date IS NOT NULL) AND (CONVERT(VARCHAR(10), LIS_Results.Final_Report_Date, 101) BETWEEN #ReportStartDate AND #ReportEndDate)
Use an if statement in tsql to say if parameter is yes select records from table where conditions are true.
If no select records from table where conditions are true and date field is not null.
Since #PrelimOnly can only be YES or NO, use:
SELECT
...
FROM
...
WHERE #PrelimOnly = 'NO' or LIS_Results.Prelim_Report_Date is not null
...
If the parameter is NO, the left hand condition of the OR is satisfied and all rows are returned, otherwise only those non-null rows will be returned, as required.
here exist small trick:
((LIS_Results.Prelim_Report_Date <> '#PrelimOnly') OR (1=#AllowNull))
if user selected NO - set AllowNull argument to 1, other way set it to 0
NOTE: AllowNull - it is custom additional argument, you should add the same way as #PrelimOnly
another possible approach:
((LIS_Results.Prelim_Report_Date <> '#PrelimOnly') OR ('NO'='#PrelimOnly'))
for you full query you should do like this:
WHERE
(CONVERT(VARCHAR(10), LIS_Results.Final_Report_Date, 101) BETWEEN ReportStartDate AND ReportEndDate) AND
(
LIS_Results.Prelim_Report_Date is not null
or
('#PrelimOnly' = 'NO') // if instead of NO VS sends empty string replace it here
)
It was a combination of Iiya and Ian that got me the solution however the syntax was not complete and is as follows.
WHERE (#PrelimOnly = 'NO') AND (CONVERT(VARCHAR(10), LIS_Results.Final_Report_Date, 101)
BETWEEN #ReportStartDate AND #ReportEndDate) OR (LIS_Results.Prelim_Report_Date IS NOT NULL)
AND (CONVERT(VARCHAR(10), LIS_Results.Final_Report_Date, 101) BETWEEN #ReportStartDate AND
#ReportEndDate)
It requeired the Date and Time paramater to be repeated so that both paramters would still work, and the #PrelimOnly = 'NO' had to be first.

SSRS and string parsing to produce a report

A friend told me that his new employer needs an SSRS report that parse a column that contains n consecutive occurrences of
1) the literal "Date:"
2) An optional separator character
3) followed by a date in DD-MM-YY format (leading zeros are optional)
4) a separator space
5) A single "WORD" of data that is associated with the date. This word will have no embedded spaces.
I'll populate a Sample table with data that meets this critera to give you an example to make it clear:
CREATE TABLE [dbo].[Sample](
[RowNumber] [int] NOT NULL,
[DataMess] [varchar](max) NOT NULL
) ON [PRIMARY]
INSERT [dbo].[Sample] ([RowNumber], [DataMess]) VALUES (1, N'Date:12-21-13 12/13/14/15 Date:4-2-11 39/12/134/14 Date:4-1-13 19/45/5/12')
INSERT [dbo].[Sample] ([RowNumber], [DataMess]) VALUES (2, N'Date:7-21-13 12/13/14/15 Date:8-21-12 39/12/34/14 Date:12-1-13 19/4/65/12')
INSERT [dbo].[Sample] ([RowNumber], [DataMess]) VALUES (3, N'Date:3-21-13 12/11233/14/15 Date:4-28-13 39/12/34/14 Date:9-19-13 19/45/65/12')
For the first record, "12/13/14/15" is considered to be the "Word" of data that is associated with the Date 12-21-13.
He was aked to produce the following report in SSRS:
Row Number DataMess
1 Date: 12-21-13 12/13/14/15
Date: 4-1-13 19/45/5/12
Date: 4-2-11 39/12/134/14
2 Date:12-1-13 19/4/65/12
Date:7-21-13 12/13/14/15
Date:8-21-12 39/12/34/14
3 Date:9-19-13 19/45/65/12
Date:4-28-13 39/12/34/14
Date:3-21-13 12/11233/14/15
Note that the Dates for each source row number are sorted in descending arder alomng with the associated wor of data.
I don't know SSRS, but my reaction was to recommend to him that he not even attempt the task but to tell his employer that the data shouldn't really be trying to do all of that ugly string parsing with T-SQL. Instead this repeating "Date: DATA" should be stored in individual child records that are associated with a parent Row record. I believe that the code would be ugly, inefficient, brittle and hard to maintain. What are your thoughts?
Assuming that management\client is always right or to conceed that "ideally" this is correct, but "for now" we need a SQL that produces the following report, how would one do this? The expectation was that this can be produced quickly ( a half day, for example)
You are of course correct, it's certainly far from the best way of storing the data. Any way of extracting the data for this report will be much more complex than it would be if it was stored differently.
However, based on the data it still wouldn't be too tough to actually generate the report. Due to the table structure actually generating the dataset for the report would be the hardest part.
So to generate the dataset, you need to split the data in DataMess to get one row per Date/Word, and be able to extract the date from that split data to be able to order by date as required.
Take your pick on how you want to split the data:
Split function equivalent in T-SQL? has many options, as does this link - Best Split Function.
Here's a SQL Fiddle with one of the functions in action.
Once you've split the data, use the appropriate function to extract the date portion, i.e. between the colon and the space before the word data, then CAST this as a date.
Once you've actually got the dataset, it's the most trivial of reports - just add a row group based on RowNumber, add the split Date/Word data as a detail field and you're done. Make sure the dataset is ordered by the extracted date field, even though you don't actually display this in the report.
As an interim measure I would certainly expect this to be doable in half a day of work or so. So for just this report it's not too bad, but for anything else you'll probably have trouble.
For a few rows it will likely run fine, but on any non-trivial sized dataset performance will be suboptimal.
Thank you. Here's what I did for the remaining part to get the dates sorted in DESC order.
SELECT
RowNumber
,'Date: ' + ss.Item AS Data
--,cast(substring(ss.Item, 1, charindex(' ' , ss.Item) ) AS date)
FROM
Sample s
CROSS apply dbo.SplitStrings_XML(s.DataMess,
N'Date:') ss
WHERE
Item IS NOT NULL
ORDER BY
rownumber,
cast(substring(ss.Item, 1, charindex(' ' , ss.Item) ) AS date) desc
If the data fails to hold up to this expected format and we encounter a date that is not valid, then the whole report blows up.