SSRS: Concatenate field's distinct values - reporting-services

How can I concatenate distinct values under a column in a dataset, and display the result in a table or textbox? For example:
Dataset:
+----+------+
| ID | Pet |
+----+------+
| 1 | Cat |
+----+------+
| 2 | Dog |
+----+------+
| 3 | Frog |
+----+------+
| 4 | Dog |
+----+------+
Result:
+-----+----------------+
| Pet | Cat, Dog, Frog |
+-----+----------------+
All I have found online is using the Join + LookUpSet. I found this answer and tried:
=Join(LookUpSet(Fields!Pet.Value, Fields!Pet.Value, Fields!Pet.Value, "PetsDS"), ", ")
However, it only gives me the first item.

You could try to use query like below to see whether it works or not
create table #jj (name varchar(20), id int)
insert into #jj values ('a', 1), ('s',2),('d',3),('a',4)
SELECT STUFF((SELECT distinct ', ' +name FROM #jj FOR XML PATH('')),1,1,'') as [Currency]

Related

How to calculate count of each value in MySQL JSON array?

I have a MySQL table with the following definition:
mysql> desc person;
+--------+---------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+--------+---------+------+-----+---------+-------+
| id | int(11) | NO | PRI | NULL | |
| name | text | YES | | NULL | |
| fruits | json | YES | | NULL | |
+--------+---------+------+-----+---------+-------+
The table has some sample data as follows:
mysql> select * from person;
+----+------+----------------------------------+
| id | name | fruits |
+----+------+----------------------------------+
| 1 | Tom | ["apple", "orange"] |
| 2 | John | ["apple", "mango"] |
| 3 | Tony | ["apple", "mango", "strawberry"] |
+----+------+----------------------------------+
How can I calculate the total number of occurrences for each fruit? For example:
+------------+-------+
| fruit | count |
+------------+-------+
| apple | 3 |
| orange | 1 |
| mango | 2 |
| strawberry | 1 |
+------------+-------+
Some research shows that the JSON_LENGTH function can be used but I cannot find an example similar to my scenario.
You can use JSON_EXTRACT() function to extract each value ("apple", "mango", "strawberry" and "orange") of all three components of the arrays, and then then apply UNION ALL to combine all such queries:
SELECT comp, count(*)
FROM
(
SELECT JSON_EXTRACT(fruit, '$[0]') as comp FROM person UNION ALL
SELECT JSON_EXTRACT(fruit, '$[1]') as comp FROM person UNION ALL
SELECT JSON_EXTRACT(fruit, '$[2]') as comp FROM person
) q
WHERE comp is not null
GROUP BY comp
Indeed If your DB's version is 8, then you can also use JSON_TABLE() function :
SELECT j.fruit, count(*)
FROM person p
JOIN JSON_TABLE(
p.fruits,
'$[*]' columns (fruit varchar(50) path '$')
) j
GROUP BY j.fruit;
Demo
You can't do it without first creating a table with one row per fruit.
CREATE TABLE allfruits (fruit VARCHAR(10) PRIMARY KEY);
INSERT INTO allfruits VALUES ('apple'), ('orange'), ('mango'), ('strawberry');
There is not a good way to generate this from the JSON.
Once you have that table, you can join it to the JSON and then use GROUP BY to count the occurrences.
SELECT fruit, COUNT(*) AS count
FROM allfruits
JOIN person ON JSON_SEARCH(person.fruits, 'one', fruit) IS NOT NULL
GROUP BY fruit;
Output:
+------------+-------+
| fruit | count |
+------------+-------+
| apple | 3 |
| mango | 2 |
| orange | 1 |
| strawberry | 1 |
+------------+-------+
Note that it will do a table-scan on the person table to find each fruit. This is pretty inefficient, and as your person table gets larger, it will become a performance problem.
If you want to optimize for this type of query, then you shouldn't use JSON to store an array of fruits. You should store data in a normalized way, representing the many-to-many relationship between persons and fruits with another table.
This is related to my answer to Is storing a delimited list in a database column really that bad?
I think the simplest solution would be to use JSON_TABLE function.
The query you need is
select ft.fruit, count(ft.fruit) from person,
json_table(
fruits,
'$[*]' columns(
fruit varchar(128) path '$'
)
) as ft
group by ft.fruit
;
You can find working example in this dbfiddle
Fruit demo

How to return Name with its specific ID?

Suppose we have a table like this
+------------+-----------+
| EmployeeID | Name |
+------------+-----------+
| 1 | Peter |
| 2 | John |
| 3 | Henry |
And now I want to return all the name with their primary key. Peter(1),John(2),Henry(3),...
+------------+
| Test |
+------------+
| Peter(1) |
| John(2) |
| Henry(3) |
I tried some specific SELECT statements but I didn't get the result I wanted
Use CONCAT function of mysql:
SELECT CONCAT(A.Name, '(', A.EmployeeID, ')') AS Test FROM myTable AS A;
Use concat to join strings together.
SELECT
Concat(`Name`,'(', employeeID, ')') AS EmployeeWithId
FROM EmployeeTable
Beware, concat will return null if any of the used columns are null.
On the other hand, concat_ws(delimiter, string1, string2) will return a string even if any of the used columns are null.
SELECT
concat_ws('',`Name`,'(', employeeID, ')') AS EmployeeWithId
FROM EmployeeTable
If you want the string formatted ad Name(1) you could use concat
select concat(Name, '(',EmployeeID,')') test
from myTable

Removing duplicate rows in MySQL by merging info

I have a large table with person info. Every record has an ID and is referenced by other tables. I noticed that a lot of records have duplicate keys, but they vary in the amount of information in the other fields. I'd like to merge the info in various fields into one and make that the 'master' record and all references to the other records need to be replaced with the master record.
An example
| id | key1 | key2 | name | city | dob |
|--- | ---- | ---- | ---- | ---- | -------- |
| 1 | 1 | 2 | John | | |
| 2 | 1 | 2 | | Town | |
| 3 | 1 | 2 | John | | 70/09/12 |
I need to end up with a single record (id is either 1, 2 or 3) with values
key1 = 1, key2 = 2, name = John, city = Town, dob = 70/09/12.
Is there a clever way to merge these records without testing for every field (my actual table has a lot of fields)?
You can use MAX() to get the non-empty values for each key.
SELECT key1, key2, MAX(id) AS id, MAX(name) AS name, MAX(city) AS city, MAX(dob) AS dob
FROM yourTable
GROUP BY key1, key2
If there can be different values between rows, and you don't want to include them, you can add:
HAVING COUNT(DISTINCT NULLIF(name, ''), NULLIF(city, ''), NULLIF(dob, '')) = 1

Mysql: Display query results not containing text patterns from another table

Trying to display query result only if a text pattern from a table does not appears in the product name.
+-----------------------------------------+ +--------+
| Product Name | |pattern |
+-----------------------------------------+ +--------+
|Gangster Barbie with guns & accessories | | Gun |
|Very Safe Playdoh | | Drug |
|Star Wars Lego | | nam |
|Transformers Decepticon Druglord | | |
|GTA: Namcat Version | | |
+-----------------------------------------+ +--------+
Would like to have result:
+-----------------------------------------+
| Product Name |
+-----------------------------------------+
|Very Safe Playdoh |
|Star Wars Lego |
+-----------------------------------------+
I've tried LIKE or INSTR such as:
select `Product_Name`
from Product_table
where NOT LIKE '%'+(select `text_pattern`.`Keywords` from `text_pattern`)+'%';
but none seems to work properly. Could someone please help or point in the right direction?
Here is one method using not exists:
select p.*
from product p
where not exists (select 1
from patterns pat
where p.name like concat('%', pat.pattern, '%')
);
Note: MySQL does not use + for string concatenation. It uses the concat() function.
E.g.
DROP TABLE IF EXISTS product;
CREATE TABLE product
(name VARCHAR(50) NOT NULL PRIMARY KEY);
INSERT INTO product VALUES
('Gangster Barbie with guns & accessories'),
('Very Safe Playdoh'),
('Star Wars Lego'),
('Transformers Decepticon Druglord'),
('GTA: Namcat Version');
DROP TABLE IF EXISTS patterns;
CREATE TABLE patterns
(pattern VARCHAR(12) PRIMARY KEY);
INSERT INTO patterns VALUES
('Gun'),('Drug'),('nam');
SELECT * FROM product a LEFT JOIN patterns b ON a.name LIKE CONCAT('%',b.pattern,'%');
+-----------------------------------------+---------+
| name | pattern |
+-----------------------------------------+---------+
| Gangster Barbie with guns & accessories | Gun |
| GTA: Namcat Version | nam |
| Star Wars Lego | NULL |
| Transformers Decepticon Druglord | Drug |
| Very Safe Playdoh | NULL |
+-----------------------------------------+---------+

SQL select statement optimizing (id, parent_id, child_ids)

we have a very old custom db (oracle, mysql, derby) with the restrictions: no new table fileds, no views, no functions, no procedures.
My table MYTABLE:
| id | ... | parent_id |
------------------------
| 1 | ... | |
| 2 | ... | 1 |
| 3 | ... | 1 |
| 4 | ... | 2 |
| 5 | ... | 1 |
and I my first statement:
select * from MYTABLE where id in ('1','2','3','4','5');
give my 5 records.
Then I need the information about the first (no deeper) child ids.
My current solution:
for (record in records) {
// get child ids as comma separated string list
// e.g. "2,3,5" for id 1
String childIds = getChildIds(record.id);
}
with the second statement in getChildIds(record.Id):
select id from MYTABLE where parent_id='record.Id';
So I have 1 + 5 = 6 statements for the required information.
I'm looking for a solution to select the records from the following "imaginary" table with the "imaginary" field "child_ids":
| id | ... | parent_id | child_ids |
------------------------------------
| 1 | ... | | 2,3,5 |
| 2 | ... | 1 | 4 |
| 3 | ... | 1 | |
| 4 | ... | 2 | |
| 5 | ... | 1 | |
Does anyone have an idea how I can get this information with only one statement (or with 2 statements)?
Thanks for your help, Thomas
FOR MYSQL:
How about using the GROUP_CONCAT() function like the following:
SELECT id, parent_id,
GROUP_CONCAT(child_id ORDER BY child_id SEPARATOR ',') AS child_ids
FROM MYTABLE
WHERE id IN ('1','2','3','4','5')
FOR ORACLE:
If you have a later version of Oracle you could use the LISTAGG() function:
SELECT parent_id,
LISTAGG(child_id, ', ') WITHIN GROUP (ORDER BY child_id) "child_ids"
FROM MYTABLE
WHERE id IN ('1','2','3','4','5')
GROUP BY parent_id
FOR DERBY:
I don't know anything about derby, but doing a little research it uses IBM DB2 SQL syntax. So, maybe using a combination of XMLSERIALIZE(), XMLAGG(), and XMLTEXT() will work for you:
SELECT parent_id,
XMLSERIALIZE(XMLAGG(XMLTEXT(child_id) ORDER BY child_id) AS CLOB(30K))
FROM table GROUP BY parent_id