TSQL - Merge Group with Range? - tsql

Example code:
Declare #table1 TABLE(myIndex int identity(1,1),[cal] int, Name Nvarchar(20) );
Declare #range int = 5;
INSERT INTO #table1 ([cal], Name)
VALUES (1, 'A'), (3, 'B'), (4, 'C'), (2, 'D'), (3, 'E'), (4, 'F'), (6, 'G'), (2, 'H');
SELECT * FROM #table1
Output:
myIndex | Sum(cal) | Name |
--------+----------+------+
1 | 1 | A |
2 | 3 | B |
3 | 4 | C |
4 | 2 | D |
5 | 3 | E |
6 | 4 | F |
7 | 6 | G |
8 | 2 | H |
I wan to Sum(cal) > 5 then join string
TSQL - 2012 - Report Expect Example
myIndex | Sum(cal) | Name | Description
--------+----------+--------+--------------------------------
1 | 7 | A,B,C | (Explain: First Sum(cal) > 5, Merge String)
2 | 9 | D,E,F | (Explain:Second Sum(cal) > 5, Merge String)
3 | 6 | G | (Explain:Third, Sum(cal) > 5, Merge String)
4 | 2 | H | (Explain:Last, still one last step)
Please, help me to resolve the problems.

This is a solution using a cursor. Hope this could help. Try it and see if performance could be acceptable.
Declare #table1 TABLE(myIndex int identity(1,1),[cal] int, Name Nvarchar(20) );
Declare #range int = 5;
INSERT INTO #table1 ([cal], Name)
VALUES (1, 'A'), (3, 'B'), (4, 'C'), (2, 'D'), (3, 'E'), (4, 'F'), (6, 'G'), (2, 'H');
SELECT * FROM #table1
-----
DECLARE #aggregates TABLE (myIndex int identity(1,1),SumCal int, AggregateNames Nvarchar(MAX) );
DECLARE #SumCal INT
, #AggregateNames NVARCHAR(MAX)
, #cal INT
, #Name Nvarchar(20)
;
SET #SumCal = 0;
SET #AggregateNames = NULL;
DECLARE cur CURSOR LOCAL FAST_FORWARD FOR
SELECT [cal], name from #table1 ORDER BY myIndex
OPEN cur
FETCH NEXT FROM cur INTO #cal, #Name
WHILE ##FETCH_STATUS = 0
BEGIN
SET #SumCal = #SumCal + #cal
SET #AggregateNames = ISNULL(#AggregateNames + ',', '') + #Name
IF #SumCal > 5
BEGIN
INSERT INTO #aggregates([SumCal], AggregateNames)
VALUES(#SumCal, #AggregateNames)
SET #SumCal = 0
SET #AggregateNames = NULL
END
FETCH NEXT FROM cur INTO #cal, #Name
END
IF #SumCal > 0
BEGIN
INSERT INTO #aggregates([SumCal], AggregateNames)
VALUES(#SumCal, #AggregateNames)
END
CLOSE cur
DEALLOCATE cur
SELECT * FROM #aggregates

Related

Postgres Query to Generate a Table Matrix

I am attempting to generate a "matrix" (I may be using the term incorrectly here) from 3 tables using a Postgres query.
How can I achieve this using such an SQL query?
Here are the example tables I have at the moment:
Company
+----+-------------+
| id | name |
+----+-------------+
| 1 | 9999999991 |
| 2 | 9999999992 |
| 3 | 9999999993 |
| 4 | 9999999994 |
| 5 | 9999999995 |
| 6 | 9999999996 |
| 7 | 9999999997 |
| 8 | 9999999998 |
+----+-------------+
Services
+----+-------------+
| id | name |
+----+-------------+
| 1 | Service 1 |
| 2 | Service 2 |
| 3 | Service 3 |
| 4 | Service 4 |
+----+-------------+
Service Company Map
+----+----------+---------------+
| id |company | services |
+----+-----------+--------------+
| 1 | 9999999991| 2 |
| 2 | 9999999991| 4 |
| 3 | 9999999992| 1 |
| 4 | 9999999992| 4 |
| 5 | 9999999993| 1 |
| 6 | 9999999993| 3 |
| 7 | 9999999993| 4 |
+----+-----------+--------------+
Here is an example of the matrix I am attempting to generate
+----------+----------+----------+----------+----------+
| | Service 1| Service 2| Service 3| Service 4|
+----------+----------+----------+----------+----------+
| Company 1| - | X | - | X |
| Company 2| X | - | - | X |
| Company 3| X | - | X | X |
| Company 4| - | - | - | - |
+----------+----------+----------+----------+----------+
(Note, I did reference this question, but we seem to be after different things: Postgres query for matrix table)
Update: Basic DDL / Inserts per #SQLpro's request
CREATE TABLE service_client_mappings
( id int NOT NULL,
company_id int NOT NULL,
service_id int NOT NULL,
CONSTRAINT service_client_mappings_pk PRIMARY KEY (id)
);
CREATE TABLE services
( id int NOT NULL,
name char(50) NOT NULL,
CONSTRAINT services_pk PRIMARY KEY (id)
);
CREATE TABLE company
( id int NOT NULL,
name char(50) NOT NULL,
CONSTRAINT company_pk PRIMARY KEY (id)
);
INSERT INTO company
(id, name)
VALUES
(1, 'ACME');
INSERT INTO company
(id, name)
VALUES
(2, 'Target');
INSERT INTO company
(id, name)
VALUES
(3, 'Walmart');
INSERT INTO services
(id, name)
VALUES
(1, 'Service A');
INSERT INTO services
(id, name)
VALUES
(2, 'Service B');
INSERT INTO services
(id, name)
VALUES
(3, 'Service C');
INSERT INTO services
(id, name)
VALUES
(4, 'Service D');
INSERT INTO service_client_mappings
(id, company_id, service_id)
VALUES
(1, 1, 2);
INSERT INTO service_client_mappings
(id, company_id, service_id)
VALUES
(1, 1, 4);
INSERT INTO service_client_mappings
(id, company_id, service_id)
VALUES
(1, 2, 1);
INSERT INTO service_client_mappings
(id, company_id, service_id)
VALUES
(1, 3, 2);
INSERT INTO service_client_mappings
(id, company_id, service_id)
VALUES
(1, 3, 3);
SELECT name,
CASE WHEN EXISTS(SELECT *
FROM services AS S
JOIN service_client_mappings AS m
ON S.id = m.service_id
WHERE s.name = 'Service A'
AND c.id = m.company_id)
THEN 'X'
ELSE '-'
END AS "Service A",
CASE WHEN EXISTS(SELECT *
FROM services AS S
JOIN service_client_mappings AS m
ON S.id = m.service_id
WHERE s.name = 'Service B'
AND c.id = m.company_id)
THEN 'X'
ELSE '-'
END AS "Service B",
CASE WHEN EXISTS(SELECT *
FROM services AS S
JOIN service_client_mappings AS m
ON S.id = m.service_id
WHERE s.name = 'Service C'
AND c.id = m.company_id)
THEN 'X'
ELSE '-'
END AS "Service C",
CASE WHEN EXISTS(SELECT *
FROM services AS S
JOIN service_client_mappings AS m
ON S.id = m.service_id
WHERE s.name = 'Service D'
AND c.id = m.company_id)
THEN 'X'
ELSE '-'
END AS "Service D"
FROM company AS c;
By the way your adata are incorrect... For INSERT INTO service_client_mappings, you should have :
INSERT INTO service_client_mappings
(id, company_id, service_id)
VALUES
(1, 1, 2);
INSERT INTO service_client_mappings
(id, company_id, service_id)
VALUES
(2, 1, 4);
INSERT INTO service_client_mappings
(id, company_id, service_id)
VALUES
(3, 2, 1);
INSERT INTO service_client_mappings
(id, company_id, service_id)
VALUES
(4, 3, 2);
INSERT INTO service_client_mappings
(id, company_id, service_id)
VALUES
(5, 3, 3);
Result is :
name Service A Service B Service C Service D
-------------- --------- --------- --------- ---------
ACME - X - X
Target X - - -
Walmart - X X -
In addition, to build this with a variable number of services you can use dynamic sql...
A first way to do this is :
SELECT CONCAT(
'SELECT name,
CASE WHEN EXISTS(SELECT *
FROM services AS S
JOIN service_client_mappings AS m
ON S.id = m.service_id
WHERE s.name = ''', name, '''
AND c.id = m.company_id)
THEN ''X''
ELSE ''-''
END AS "', name , '",') AS SQL_STRING
FROM services;

How to set values from recursive query in PostgreSQL?

I have a query which gives a result:
id | manager_id | level | star_level
----+------------+-------+------------
1 | NULL | 1 | 0
2 | 1 | 2 | 1
3 | 2 | 3 | 1
4 | 3 | 4 | 2
5 | 4 | 5 | 2
6 | 5 | 6 | 2
7 | 6 | 7 | 3
8 | 7 | 8 | 3
9 | 8 | 9 | 4
(9 rows)
Here is the query:
WITH RECURSIVE parents AS (
SELECT e.id
, e.manager_id
, 1 AS level
, CAST(s.is_star AS INTEGER) AS star_level
FROM employees AS e
INNER JOIN skills AS s
ON e.skill_id = s.id
WHERE manager_id IS NULL
UNION ALL
SELECT e.id
, e.manager_id
, p.level + 1 AS level
, p.star_level + CAST(s.is_star AS INTEGER) AS star_level
FROM employees AS e
INNER JOIN skills AS s
ON e.skill_id = s.id
INNER JOIN parents AS p
ON e.manager_id = p.id
WHERE e.manager_id = p.id
)
SELECT *
FROM parents
;
Can you please tell me how you can change the query so that in the same query the level and star_level values ​​can be written to the corresponding columns?
Demo data:
create table Employees(
id INT,
name VARCHAR,
manager_id INT,
skill_id INT,
level INT,
star_level INT
);
create table Skills(
id INT,
name VARCHAR,
is_star BOOL
);
INSERT INTO Employees
(id, name, manager_id, skill_id)
VALUES
(1, 'Employee 1', NULL, 1),
(2, 'Employee 2', 1, 2),
(3, 'Employee 3', 2, 3),
(4, 'Employee 4', 3, 4),
(5, 'Employee 5', 4, 5),
(6, 'Employee 6', 5, 1),
(7, 'Employee 7', 6, 2),
(8, 'Employee 8', 7, 3),
(9, 'Employee 9', 8, 4)
;
INSERT INTO Skills
(id, name, is_star)
VALUES
(1, 'Skill 1', FALSE),
(2, 'Skill 2', TRUE),
(3, 'Skill 3', FALSE),
(4, 'Skill 4', TRUE),
(5, 'Skill 5', FALSE)
;
As a result, I need a query which will count level and star_level columns for Employees table and write their values (in Employees table) in one query.
You can use an UPDATE statement together with your CTE:
with recursive parents as (
... your original query goes here ...
)
update employees
set level = p.level,
star_level = p.star_level
from parents p
where employees.id = p.id;

How to bulk update while overriding "CREATE UNIQUE INDEX" constraint?

I have table
CREATE TABLE my_table (
ranking INTEGER NOT NULL,
name TEXT NOT NULL,
is_deleted BOOLEAN NOT NULL
);
CREATE UNIQUE INDEX exists_const ON my_table ranking WHERE (is_deleted = FALSE);
| ranking | name | is_deleted |
| 1 | A | true |
| 2 | B | true |
| 1 | C | true |
| 1 | D | false |
| 2 | E | false |
| 3 | F | false |
So when I want to update a new ranking for D, E, F to be
UPDATE "my-table" AS t SET "ranking"=v."ranking" FROM
(VALUES(1, 'F', 'false'),(2, 'D', 'false'), (3, 'E', 'false'))
AS v("ranking","name", 'is_deleted') WHERE v.name = t.name
| 1 | F | false |
| 2 | D | false |
| 3 | E | false |
I have duplicate key value violates unique constraint "exists_const" ERROR.
Even though the new rows will not have duplicates at all. How can I perform such update?
The problem can be solved by Deferring the check at the end of the transaction, but you cannot do it with INDEX (as far as I know). The workaround I can give is to use EXCLUDE as described below:
CREATE TABLE my_table (
ranking INTEGER NOT NULL,
name TEXT NOT NULL,
is_deleted BOOLEAN NOT NULL,
EXCLUDE USING BTREE (ranking WITH =) WHERE (not is_deleted) DEFERRABLE
);
Attention to the 'DEFERRABLE'
Here to populate initial data:
insert into my_table ( ranking, name, is_deleted)
values
(1, 'A', true),
(2, 'B', true),
(1, 'C', true),
(1, 'D', false),
(2, 'E', false),
(3, 'F', false);
Now if you execute :
UPDATE "my_table" AS t
SET "ranking" = v."ranking"
FROM
(VALUES(1, 'F', 'false'),(2, 'D', 'false'), (3, 'E', 'false'))
AS v("ranking","name", "is_deleted")
WHERE v.name = t.name;
It should work.
And to test the uniqueness I tested it with :
insert into my_table ( ranking, name, is_deleted)
values
(3, 'G', false);

How do I select a postgres Many-to-One relationship as a single row? [duplicate]

This question already has answers here:
PostgreSQL Crosstab Query
(7 answers)
Closed 3 years ago.
I have a many-to-one relationship between Animals and their attributes. Because different Animals have different attributes, I want to be able to select all animals with their attribute name as a column header and NULL values where that animal does not have that attribute.
Like so...
TABLE_ANIMALS
ID | ANIMAL | DATE | MORE COLS....
1 | CAT | 2012-01-10 | ....
2 | DOG | 2012-01-10 | ....
3 | FROG | 2012-01-10 | ....
...
TABLE_ATTRIBUTES
ID | ANIMAL_ID | ATTRIBUE_NAME | ATTRIBUTE_VALUE
1 | 1 | noise | meow
2 | 1 | legs | 4
3 | 1 | has_fur | TRUE
4 | 2 | noise | woof
5 | 2 | legs | 4
6 | 3 | noise | croak
7 | 3 | legs | 2
8 | 3 | has_fur | FALSE
...
QUERY RESULT
ID | ANIMAL | NOISE | LEGS | HAS_FUR
1 | CAT | meow | 4 | TRUE
2 | DOG | woof | 4 | NULL
3 | FROG | croak | 2 | FALSE
How would I do this? To reiterate, it's important that all the columns are there even if one Animal doesn't have that attribute, such as "DOG" and "HAS_FUR" in this example. If it doesn't have the attribute, it should just be null.
How about a simple join, aggregation and group by?
create table table_animals(id int, animal varchar(10), date date);
create table table_attributes(id varchar(10), animal_id int, attribute_name varchar(10), attribute_value varchar(10));
insert into table_animals values (1, 'CAT', '2012-01-10'),
(2, 'DOG', '2012-01-10'),
(3, 'FROG', '2012-01-10');
insert into table_attributes values (1, 1, 'noise', 'meow'),
(2, 1, 'legs', 4),
(3, 1, 'has_fur', TRUE),
(4, 2, 'noise', 'woof'),
(5, 2, 'legs', 4),
(6, 3, 'noise', 'croak'),
(7, 3, 'legs', 2),
(8, 3, 'has_fur', FALSE);
select ta.animal,
max(attribute_value) filter (where attribute_name = 'noise') as noise,
max(attribute_value) filter (where attribute_name = 'legs') as legs,
max(attribute_value) filter (where attribute_name = 'has_fur') as has_fur
from table_animals ta
left join table_attributes tat on tat.animal_id = ta.id
group by ta.animal
Here's a rextester sample
Additionally you can change the aggregation to MAX CASE WHEN... but MAX FILTER WHERE has better performance.

Select statement with join, or subquery limit

For few days now I'm trying to solve this problem.
I have table group_user, group_name.
What I wanna to do is select user groups, than description that group (from group_name), and 10 other users from the group.
It's not problem with first two. The problem is, that I'm nowhere to get limit users.
I can select user_group, and other users in that group. I don't know how to limit that.
Using:
SELECT a.g_id,b.group,b.userid
FROM group_user AS a
RIGHT JOIN
(SELECT g_id as group, u_id as userid FROM group_user) AS b ON a.g_id=b.group
WHERE u_id=112
It showing me, my user groups and users in that group. But when I'm trying to limit in subwuery, it limits all, not particular group.
I tried, Select users, with using IN where was goups of my user without luck.
I was thinking maybe group and having will help, but I can't see how I could use it.
So my question is, how can I limit subquery result in MySQL where the subquery is built on result of query.
I think im overload and maybe I don't see something.
UPDATE to show what I really wanna accomplish here's another piece of code.
SELECT g_id FROM group_user WHERE user_id = 112
So I get all groups that user is in let, saye each of that select is var extra_group, so second query will be
SELECT u_id FROM group_user WHERE group_id = extra_group LIMIT 10
I need to do same as above, in one query.
another UPDATE after MIKE post.
I should ADD that, user can be in more than 1 group. So I think the real problem is, that I don't have any clue how to select those groups and in same query select 10 users for selected groups, so in result could be
g_id u_id
1 | 2
1 | 3
1 | 4
3 | 3
3 | 8
where g_id is user groups from that query
SELECT g_id FROM group_user WHERE user_id = 112
Create sample tables and add data:
CREATE TABLE `group_user` (
`u_id` int(11) DEFAULT NULL,
`g_id` int(11) DEFAULT NULL,
`apply_date` date DEFAULT NULL
);
CREATE TABLE `group_name` (
`g_id` int(11) DEFAULT NULL,
`g_name` varchar(255) DEFAULT NULL
);
INSERT INTO `group_name` VALUES
(1, 'Group 1'), (2, 'Group 2'), (3, 'Group 3'), (4, 'Group 4'), (5, 'Group 5');
INSERT INTO `group_user` VALUES
(1, 1, '2010-12-01'), (1, 2, '2010-12-01'), (1, 3, '2010-12-01'), (1, 4, '2010-12-01'), (1, 5, '2010-12-01'),
(2, 1, '2010-12-02'), (2, 2, '2010-12-02'),
(3, 1, '2010-12-03'), (3, 2, '2010-12-03'), (3, 3, '2010-12-03'), (3, 4, '2010-12-03'),
(4, 1, '2010-12-04'), (4, 2, '2010-12-04'),
(5, 1, '2010-12-05'), (5, 2, '2010-12-05'),
(6, 1, '2010-12-06'), (6, 2, '2010-12-06'),
(7, 1, '2010-12-07'), (7, 2, '2010-12-07'), (7, 3, '2010-12-07'), (7, 4, '2010-12-07'), (7, 5, '2010-12-07'),
(8, 1, '2010-12-08'), (8, 2, '2010-12-08'),
(9, 1, '2010-12-09'), (9, 2, '2010-12-09'), (9, 3, '2010-12-09'), (9, 4, '2010-12-09'), (9, 5, '2010-12-09');
Select the groups of which user u_id == 1 is a member. Then for each group select a maximum of 4 members (excluding user u_id == 1), ordered by descending apply_date:
SELECT u3.g_id, g.g_name, u3.u_id, u3.apply_date
FROM (
SELECT
u1.g_id,
u1.u_id,
u1.apply_date,
IF( #prev_gid <> u1.g_id, #user_index := 1, #user_index := #user_index + 1 ) AS user_index,
#prev_gid := u1.g_id AS prev_gid
FROM group_user AS u1
JOIN (SELECT #prev_gid := 0, #user_index := NULL) AS vars
JOIN group_user AS u2
ON u2.g_id = u1.g_id
AND u2.u_id = 1
AND u1.u_id <> 1
ORDER BY u1.g_id, u1.apply_date DESC, u1.u_id
) AS u3
JOIN group_name AS g ON g.g_id = u3.g_id
WHERE u3.user_index <= 4
ORDER BY u3.g_id, u3.apply_date DESC, u3.u_id;
+------+---------+------+------------+
| g_id | g_name | u_id | apply_date |
+------+---------+------+------------+
| 1 | Group 1 | 5 | 2010-12-05 |
| 1 | Group 1 | 4 | 2010-12-04 |
| 1 | Group 1 | 3 | 2010-12-03 |
| 1 | Group 1 | 2 | 2010-12-02 |
| 2 | Group 2 | 5 | 2010-12-05 |
| 2 | Group 2 | 4 | 2010-12-04 |
| 2 | Group 2 | 3 | 2010-12-03 |
| 2 | Group 2 | 2 | 2010-12-02 |
| 3 | Group 3 | 9 | 2010-12-09 |
| 3 | Group 3 | 7 | 2010-12-07 |
| 3 | Group 3 | 3 | 2010-12-03 |
| 4 | Group 4 | 9 | 2010-12-09 |
| 4 | Group 4 | 7 | 2010-12-07 |
| 4 | Group 4 | 3 | 2010-12-03 |
| 5 | Group 5 | 9 | 2010-12-09 |
| 5 | Group 5 | 7 | 2010-12-07 |
+------+---------+------+------------+