Passing multiple values for a single parameter from SSRS to hive - reporting-services

Creating a concatenated string in SSRS with values enclosed in single quotes
Any answers to the above question?. I am struck with the same problem:
The query from SSRS side is:
select *
from xyz.test_table1
where f1 in (?)
Datasource for me in this case is a hive table. User selection on the parameter is a multivalued parameter which is what I expect to be substituted as:
where in ('value1','value2')
when query is executed. But when looked at the query execution on the hive side, it comes as:
where in ('value1,value2')
How could I solve this?

From the documentation here, it seems Hive Query Language supports Common Table Expressions.
Consequently, something similar to the following should work:
declare #str nvarchar(4000) = ?; -- String to split.
with n(n) as (select 1 union all select 1 union all select 1 union all select 1 union all select 1 union all select 1 union all select 1 union all select 1 union all select 1 union all select 1)
-- Select the same number of rows as characters in #str as incremental row numbers.
-- Cross joins increase exponentially to a max possible 10,000 rows to cover largest #str length.
,t(t) as (select top (select len(isnull(#str,'')) a) row_number() over (order by (select null)) from n n1,n n2,n n3,n n4)
-- Return the position of every value that follows the specified delimiter.
,s(s) as (select 1 union all select t+1 from t where substring(isnull(#str,''),t,1) = ',')
-- Return the start and length of every value, to use in the SUBSTRING function.
-- ISNULL/NULLIF combo handles the last value where there is no delimiter at the end of the string.
,l(s,l) as (select s,isnull(nullif(charindex(',',isnull(#str,''),s),0)-s,4000) from s)
-- Return each individual value in the delimited string along with it's position.
,v as (select row_number() over(order by s) as rn
,substring(#str,s,l) as item
from l
)
select *
from v
join xyz.test_table1 as t
on v.v = t.f1
If you rather understandably don't want this rigamarole in all of your datasets, you would need to encapsulate this logic into whatever the Hive equivalent of a SQL Server table-valued parameter is, perhaps a UDTF?
In SQL Server, the function would be defined as follows:
create function [dbo].[fn_StringSplit4k]
(
#str nvarchar(4000) = ' ' -- String to split.
,#delimiter as nvarchar(1) = ',' -- Delimiting value to split on.
,#num as int = null -- Which value to return.
)
returns table
as
return
-- Start tally table with 10 rows.
with n(n) as (select 1 union all select 1 union all select 1 union all select 1 union all select 1 union all select 1 union all select 1 union all select 1 union all select 1 union all select 1)
-- Select the same number of rows as characters in #str as incremental row numbers.
-- Cross joins increase exponentially to a max possible 10,000 rows to cover largest #str length.
,t(t) as (select top (select len(isnull(#str,'')) a) row_number() over (order by (select null)) from n n1,n n2,n n3,n n4)
-- Return the position of every value that follows the specified delimiter.
,s(s) as (select 1 union all select t+1 from t where substring(isnull(#str,''),t,1) = #delimiter)
-- Return the start and length of every value, to use in the SUBSTRING function.
-- ISNULL/NULLIF combo handles the last value where there is no delimiter at the end of the string.
,l(s,l) as (select s,isnull(nullif(charindex(#delimiter,isnull(#str,''),s),0)-s,4000) from s)
select rn
,item
from(select row_number() over(order by s) as rn
,substring(#str,s,l) as item
from l
) a
where rn = #num
or #num is null;

Figured it out! Posting the answer for other users.
Provide the query(under Query in SSRS) as an expression like below:
="select * from xyz.test_table1 where f1 in ('"&Join(Parameters!param.Value,"','")&"')"
The above string manipulation translates to:
select * from xyz.test_table1 where f1 in ('value1','value2')
Note: value1, value2 here are the values from user selected multivalue parameter

Related

How to fix Number Of Row Per Page in SSRS Report?

I Have Fix 10 Rows Per Page , If 2records Comes From Query Then I Want To Show 8 Blank Rows. How To Fix It ?
This is a fairly generic example. It just counts the actual rows, calculates how many rows are required to round up to the nearest 10 and then UNIONs a query that generates blank rows.
SELECT *
FROM (
SELECT
RowN = ROW_NUMBER() OVER(ORDER BY myColumn), -- order by any column
*
FROM myTable
UNION ALL
SELECT
TOP (SELECT ExtraRows = (FLOOR((Count(*)+9)/10) * 10) - COUNT(*) FROM myTable) -- 10 here is rows per page
NewRowNumber = ROW_NUMBER() OVER (ORDER BY [object_id]) + (SELECT COUNT(*) FROM myTable)
,NULL, NULL, NULL -- one nulll value for each column in myTable
FROM sys.all_columns
) u
ORDER by u.RowN -- add any additional required sorting here
If your current query is not simple then dump the results of that into a temp table
SELECT *
INTO #t
FROM ...
myBigQuery
then change the references to myTable in the main query above to #t or whatever the temp table is called.
EDIT for using with SP
If using a Stored proc then you can dump the results of that into a temp table and do the same. For exmaple
CREATE TABLE #t (ColA int, ColB varchar(100)....)
INSERT INTO #t
EXEC myStoredProc
...
the main query from above
...
Just swap out all references to myTable with #t

Sort a field of array upon mysql query [duplicate]

This question already has answers here:
Is storing a delimited list in a database column really that bad?
(10 answers)
Closed 2 years ago.
I have a field: userids which contains 19,2,23 for example, which I populate based on user ids added to a room arbitrarily. I list the corresponding names based on these userids.
Right now I get Richard, Evan, James from this query (19 = Richard, 2 = Evan, 23 = James).
What I would like to do is sort the array (the field) upon query so that it is 2,19,23 and the names are returned Evan, Richard, James.
So a query that looks like this:
SELECT c.*,c.NAME as PAGE, ASORT(c.USERIDS) as USERIDS, p.*
FROM TBL_CONTENT c, TBL_PAGE p
WHERE c.PAGEID = p.ID AND FIND_IN_SET(?, c.USERIDS)
Should look like (help me with this query)?
of course, the ASORT function does not exist - I just include it as an example of what I am trying to do.
Any help here?
Thank you!
This works with mysql 8
For mysql 5.x , you need much more code to do that
SET #a = '19,2,23';
SELECT GROUP_CONCAT(ord ORDER BY ord)
FROM
(select CAST(num as UNSIGNED) ord
from (SELECT #a as names) t
join json_table(
replace(json_array(t.names), ',', '","'),
'$[*]' columns (num varchar(50) path '$')
) j) z1
RESULTS in
# GROUP_CONCAT(ord ORDER BY ord)
2,19,23
For Mysql 5.x You can use a function
DELIMITER $$
CREATE DEFINER=`root`#`localhost` FUNCTION `fn_split_stringnumbers_and_sort`(_text TEXT) RETURNS text CHARSET utf8mb4
DETERMINISTIC
BEGIN
SET #text = _text;
SELECT
GROUP_CONCAT((name+0) ORDER BY (name +0)) INTO #output
FROM
(
select
SUBSTRING_INDEX(SUBSTRING_INDEX(t1.name, ',', numbers.n), ',', -1) name
from
(select 1 n union all
select 2 union all select 3 union all
select 4 union all select 5) numbers INNER JOIN (SELECT #text as name) t1
on CHAR_LENGTH(t1.name)
-CHAR_LENGTH(REPLACE(t1.name, ',', ''))>=numbers.n-1
order by n
) t2;
RETURN #output;
END$$
DELIMITER ;
SO that this query
SET #a = '19,2,23';
SELECT fn_split_stringnumbers_and_sort(#a);
has the same result
# fn_split_stringnumbers_and_sort(#a)
2,19,23
Caution
This function only would split 5 comma separated numbers if you have more than that you have to increase table with more UNION

MySQL one query grouped by n rows, max and min inside group, possible?

I think there is no question like this.
I need to group rows by n records and get some values of this group.
I think is better to explain with a graphic example:
Is possible to do a query like this? if not my solution will be make an script to create another table with this but I donĀ“t like duplicate data at all.
Thanks!!!
set #counter=-1;
select xgroup,max(x) as mx, max(y) as my, avg(value3) as v3,
from
(
select (#counter := #counter +1) as counter,
#counter div 5 as xgroup,
currency, datetime, value1, value2,
case mod(#counter,5) when 0 then value1 else 00 end as x,
case mod(#counter,5) when 4 then value2 else 00 end as y,
mod(#counter,5) as xxx
FROM findata
) name1
group by xgroup;
#jms has the right approach, but you have to be very careful when using variables:
You should not assign a variable in one expression and then reference it in another in the same select.
To work in the most recent versions of MySQL, I would suggest ordering the data in a subquery.
In addition, there are some other values that you need:
select min(col1), min(col2),
max(case when mod(rn, 5) = 0 then col3 end),
max(col4), min(col5),
max(case when mod(rn, 5) or rn = #rn then col6 end),
max(case when mod(rn, 5) or rn = #rn then col7 end)
from (select (#rn := #rn + 1) as rn, t.*
from (select t.*
from t
order by col1, col2
) t cross join
(select #rn := -1) params
) t
group by (#rn div 5);
Note the logic is a bit arcane for the last values -- this is to take into account the final group that might not have exactly 5 rows.
You need a column that looks like(assuming you want to group every 5 rows)
dummy_table
1
1
1
1
1
2
2
2
2
2
...
You can do this by using generate_series() if you are using postgre sql by using
select t1 from (select generate_series(1,x)) t1, (select generate_series(1,5)) t2;
where you can replace x by (total rows/5) i.e. for 100 rows, x = 20. If you are using any other SQL platform, you can just work on creating this dummy table accordingly.
Once you get this dummy_table, join it with your table on row_number of your table with t1 column of dummy_table(not row_number of dummy_table). Syntax for accessing row number should be straightforward.
After the join, group by this t1 column and do the required aggregation. To do this in a single query, you can do the above in an inner query and do aggregation outside it. Hope this makes sense.
Ok, thanks you all guys for your answers, thanks to it I found the simple solution.
I simply add an autoincrement column, and then I can group results by integer division by 5.
And with this query:
SELECT id,
symbol,
datetime,
open,
MAX(high),
MIN(low),
SUBSTRING_INDEX( GROUP_CONCAT(CAST(close AS CHAR) ORDER BY datetime DESC), ',', 1 ) AS close
FROM `table`
GROUP BY (id-1) DIV 5
And the resulting is:
Thanks!
A solution is to introduce some field for grouping rows for aggregative operations.
It can be reached by introducing a user-variable and assigning values that will allow to group rows as required. For example, it can be a row counter divided by grouping chuck size and rounded to nearest upper ceil number:
SET #counter=0;
SELECT CEIL((#counter:=#counter+1)/5) AS chunk, MAX(high), MIN(low) FROM `table` GROUP BY chunk;

Opposite function of find_in_set in mysql

SELECT find_in_set("1","1,2,3,4,5"); //return 1
Is there any function in mysql that can return non matching value from set of value like
SELECT find_in_set("1","1,2,3,4,5");
Expected output is : 2,3,4,5
Help me If any function.
As per my knowledge, i dont think there is any function exist in mysql that gives you result except given input.
But, You can get what you want by doing this..
you can modify it as per your requirement.
SELECT * FROM table_name WHERE NOT find_in_set("1","1,2,3,4,5");
You can always use replace():
select replace(concat(',', '1,2,3,4,5', ','), concat(',', '1', ','), '')
This puts delimiters at the beginning and end of both lists so 10 won't be confused with 100. If this isn't a problem, then you don't need the delimiters.
Here's how you can do it by using SUBSTRING_INDEX.
But make sure to put "," before and after the value otherwise it will split by value which is available in different position i.e if look for 12 it will also split where value is 123.
SELECT CONCAT(SUBSTRING_INDEX("11,22,33,44,55", ',22,', 1), ",", SUBSTRING_INDEX("11,22,33,44,55", ',22,', -1));
If you are fine with this solution then you should create a function eg. FIND_NOT_IN_SET and pass two params and return final string.
I also posted another answer on How to select all the 'not' part against the 'in' set in MySQL? using prepared statement.
Using a series of UNIONed constants instead of a single value and a comma separated field:-
SELECT a.i
FROM
(
SELECT 1 i UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5
) a
LEFT OUTER JOIN
(
SELECT 1 i
)
ON a.i = b.i
WHERE b.i IS NULL
Or if only ever 1 value you are looking for:-
SELECT a.i
FROM
(
SELECT 1 i UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5
) a
WHERE a.i != '1'

select min value of range [0,44) not in a column

I have a table with an int valued column, which has values between 0 and 43 (both included).
I would like a query that returns the min value of the range [0,44) which is not in the table.
For example:
if the table contains: 3,5, 14. The query should return 0
if the table contains: 0,1, 14. The query should return 2
if the table contains: 0,3, 14. The query should return 1
If the table contains all values, the query should return empty.
How can I achieve that?
Since the value you want is either 0 or 1 greater than a value that exists in the table, you can just do;
SELECT MIN(value)
FROM (SELECT 0 value UNION SELECT value+1 FROM MyTable) a
WHERE value < 44 AND value NOT IN (SELECT value FROM MyTable)
An SQLfiddle to test with.
One way would be to create another table that contains the integers in [0,43] and then left join that and look for NULLs, the NULLs will tell you what values are missing.
Suppose you have:
create table numbers (n int not null);
and this table contains the integers from 0 to 43 (inclusive). If your table is t and has a column n which holds the numbers of interest, then:
select n.n
from numbers n left join t on n.n = t.n
where t.n is null
order by n.n
limit 1
should give you the result you're after.
This is a fairly common SQL technique when you're working with a sequence. The most common use is probably calendar tables.
One approach is to generate a set of 44 rows with integer values, and then perform an anti-join against the distinct set of values from the table, and the grab the mininum value.
SELECT MIN(r.val) AS min_val
FROM ( SELECT 0 AS val UNION ALL
SELECT 1 UNION ALL
SELECT 2 UNION ALL
SELECT 3 UNION ALL
SELECT 4 UNION ALL
SELECT 5 UNION ALL
-- ...
SELECT 44
) r
LEFT
JOIN ( SELECT t.int_valued_col
FROM mytable t
WHERE t.int_valued_col >= 0
AND t.int_valued_col <= 43
GROUP BY t.int_valued_col
) v
ON v.int_valued_col = r.col
WHERE v.int_valued_col IS NULL
A little bit hacky and MySQL-specific:
SELECT NULLIF(MAX(IF(val=#min, #min:=(val+1), #min)), #max) as min_empty
FROM (
SELECT DISTINCT val
FROM table1
-- WHERE val BETWEEN 0 AND 43
ORDER BY val) as vals, (SELECT #min:=0, #max:=44) as init;