Tenho uma tabela no SQL Server 2016 chamada df
:
-- Create a new table with department and gender columns
CREATE TABLE df
(
country VARCHAR(50),
year INT,
val1 INT,
val2 INT,
val3 INT,
department VARCHAR(50),
gender VARCHAR(10)
);
-- Insert data into the new table, including department and gender
INSERT INTO df (country, year, val1, val2, val3, department, gender)
VALUES ('USA', 2020, 4, 4, 5, 'Sales', 'Male'),
('USA', 2020, 4, 4, 5, 'Sales', 'Male'),
('USA', 2020, 5, 5, 5, 'Sales', 'Female'),
('USA', 2020, 5, 5, 5, 'Sales', 'Female'),
('USA', 2020, 1, 1, 5, 'Sales', 'Male'),
('USA', 2020, 3, 3, 5, 'Sales', 'Female'),
('USA', 2020, 4, 2, 5, 'Sales', 'Male'),
('USA', 2020, 1, 1, 5, 'Sales', 'Female'),
('USA', 2020, 2, 2, 5, 'Sales', 'Male'),
('Canada', 2020, 2, 2, 3, 'HR', 'Female'),
('Canada', 2020, 2, 2, 3, 'HR', 'Female'),
('Canada', 2020, 2, 2, 3, 'HR', 'Male'),
('Canada', 2020, 2, 2, 3, 'HR', 'Male'),
('Canada', 2020, 5, 5, 3, 'HR', 'Female'),
('Canada', 2020, 5, 5, 3, 'HR', 'Male'),
('Canada', 2020, 1, 1, 3, 'HR', 'Female'),
('Canada', 2020, 1, 1, 3, 'HR', 'Male'),
('Canada', 2020, 3, 4, 3, 'HR', 'Female'),
('Canada', 2020, 3, 4, 3, 'HR', 'Male'),
('Canada', 2020, 5, 4, 3, 'HR', 'Female'),
('Canada', 2020, 5, 4, 5, 'HR', 'Male'),
('Canada', 2020, 5, 4, 5, 'HR', 'Female'),
('Germany', 2022, 5, 5, 4, 'IT', 'Male'),
('France', 2020, 1, 1, 2, 'Finance', 'Female'),
('France', 2020, 1, 1, 2, 'Finance', 'Female'),
('France', 2020, 3, 2, 2, 'Finance', 'Male'),
('France', 2020, 3, 4, 2, 'Finance', 'Female'),
('France', 2020, 3, 5, 5, 'Finance', 'Male'),
('France', 2020, 3, 4, 4, 'Finance', 'Female'),
('France', 2020, 3, 4, 4, 'Finance', 'Male'),
('France', 2020, 3, 4, 3, 'Finance', 'Female'),
('UK', 2021, 4, 2, 3, 'Marketing', 'Male'),
('Australia', 2022, 3, 3, 4, 'Support', 'Female'),
('Italy', 2020, 5, 5, 5, 'Operations', 'Male'),
('Italy', 2020, 5, 5, 5, 'Operations', 'Female'),
('Italy', 2020, 5, 1, 1, 'Operations', 'Male'),
('Italy', 2020, 4, 4, 1, 'Operations', 'Female'),
('Italy', 2020, 2, 1, 2, 'Operations', 'Male'),
('Italy', 2020, 3, 5, 3, 'Operations', 'Female'),
('Spain', 2021, 1, 2, 3, 'Customer Service', 'Male'),
('Mexico', 2022, 4, 4, 4, 'Logistics', 'Female'),
('Brazil', 2020, 4, 1, 1, 'R&D', 'Male'),
('Brazil', 2020, 4, 1, 1, 'R&D', 'Female'),
('Brazil', 2020, 4, 3, 4, 'R&D', 'Male'),
('Brazil', 2020, 5, 3, 5, 'R&D', 'Female'),
('Brazil', 2020, 5, 3, 5, 'R&D', 'Male'),
('Brazil', 2020, 3, 3, 1, 'R&D', 'Female'),
('Brazil', 2020, 2, 3, 1, 'R&D', 'Male');
-- Select all rows from the new table to check the data
SELECT * FROM df;
Com esta tabela, crio algumas porcentagens e uma coluna de contagem com base em alguns filtros.
-- Parameters
DECLARE @Year INT = 2020;
DECLARE @Metric VARCHAR(50) = 'count';
DECLARE @Gender VARCHAR(20) = NULL; -- Set to specific gender (e.g., 'Male', 'Female') or NULL to include all
DECLARE @Department VARCHAR(50) = NULL; -- Set to specific department (e.g., 'HR', 'Engineering') or NULL to include all
-- Set @Metric to 'dissatisfaction', 'satisfaction', or 'count'
WITH UnpivotedData AS
(
SELECT country, gender, department, year, Vals
FROM
(SELECT country, gender, department, year, val1, val2, val3
FROM df) AS SourceTable
UNPIVOT
(Vals FOR ValueColumn IN (val1, val2, val3)) AS Unpivoted
WHERE year = @Year
),
Proportions AS
(
SELECT
country,
gender,
department,
CASE
WHEN Vals = 1 THEN 'Very Dissatisfied'
WHEN Vals = 2 THEN 'Dissatisfied'
WHEN Vals = 3 THEN 'Neutral'
WHEN Vals = 4 THEN 'Satisfied'
WHEN Vals = 5 THEN 'Very Satisfied'
END AS SatisfactionLevel,
COUNT(*) * 1.0 / SUM(COUNT(*)) OVER (PARTITION BY country, gender, department) AS Proportion
FROM
UnpivotedData
GROUP BY
country, gender, department, Vals
),
Pivoted AS
(
SELECT country, gender, department,
[Very Dissatisfied],
[Dissatisfied],
[Neutral],
[Satisfied],
[Very Satisfied]
FROM Proportions
PIVOT
(MAX(Proportion)
FOR SatisfactionLevel IN ([Very Dissatisfied], [Dissatisfied], [Neutral], [Satisfied], [Very Satisfied])) AS p
),
CountryCounts AS
(
SELECT
CASE WHEN country IS NULL THEN 'Unknown' ELSE country END AS country,
gender,
department,
COUNT(*) AS Total
FROM df
WHERE year = @Year
-- Apply filters for gender and department if provided
AND (@Gender IS NULL OR gender = @Gender)
AND (@Department IS NULL OR department = @Department)
GROUP BY CASE WHEN country IS NULL THEN 'Unknown' ELSE country END, gender, department
),
OrderedData AS
(
SELECT
p.country,
p.gender,
p.department,
[Very Dissatisfied],
[Dissatisfied],
[Neutral],
[Satisfied],
[Very Satisfied],
c.Total,
CASE
WHEN @Metric = 'satisfaction' THEN ISNULL([Satisfied], 0) + ISNULL([Very Satisfied], 0)
WHEN @Metric = 'dissatisfaction' THEN ISNULL([Very Dissatisfied], 0) + ISNULL([Dissatisfied], 0)
WHEN @Metric = 'count' THEN c.Total
END AS SortValue
FROM Pivoted AS p
INNER JOIN CountryCounts AS c ON p.country = c.country AND p.gender = c.gender AND p.department = c.department
)
SELECT
country,
gender,
department,
[Very Dissatisfied],
[Dissatisfied],
[Neutral],
[Satisfied],
[Very Satisfied],
Total
FROM
OrderedData
ORDER BY
SortValue DESC;
Quero criar uma função de tabela que terá 3 argumentos:
- Métrica
- Ano
- Fator
Factor
pode ser o Gênero ou o Departamento ou ambos. Se por exemplo Factor
for o Gênero a tabela a ser agrupada pelo Gênero e se for o Departamento a ser agrupada pelo Departamento.
Se ambos forem agrupados por ambos. Se Factor
for nulo ou padrão para não ser agrupado de forma alguma.
Em relação a Year
: se o Year
for passado para ser agrupado por ano. Se o Year
for nulo, mostre todos os anos sem agrupamento.
Existe uma maneira de fazer isso no SQL Server?
Eu tenho um violino aqui
Como eu disse na sua pergunta anterior sobre SQL , você está complicando muito isso.
Você pode fazer a filtragem, desarticular e dinamizar em um único nível de CTE, e você só precisa de um nível para adicionar o
Total
, o que não seria necessário se houvesse umaID
coluna, porque então você poderia fazerCOUNT(DISTINCT ID)
.Para criar uma função, basta adicionar a
CREATE FUNCTION
sintaxe normal. Você não pode adicionarORDER BY
a uma função de tabela, é basicamente apenas uma visualização. Você precisa adicionar isso à consulta externa.Então você apenas faz
db<>violino
Note que o parâmetro sort-value não deve ser passado de uma variável ou junção lateral, pois isso deixará sua consulta muito lenta. Se for uma string constante, o otimizador pode fatorá-la.
Adicionar agrupamento dinâmico complica substancialmente isso, porque agora você precisa anular os valores antes de agrupá-los (como mostrado na outra resposta). Também será muito lento em tabelas grandes, pois você não pode usar índices. Eu recomendo fortemente que você crie funções separadas com diferentes construções de agrupamento/particionamento, alternativamente faça isso em SQL dinâmico.
Estamos aplicando substituição de valor para a coluna a ser agrupada por. Por exemplo, se o parâmetro @factorGender for nulo, agrupamos por valor,
gender
senão agrupamos por valor constanteall
- na verdade, nenhum agrupamento por gênero.Para simplificar as coisas, subquery é o seu equivalente de operação UNPIVOT-PIVOT. Podemos contar diretamente a dispersão de valores para val1,val2,val3. Expressão
ou como
conta val1=1,val2=1,val3=1 para todas as linhas.
Veja o exemplo
Atualização 1. Após o comentário de @DaleK, decidi que realmente precisava responder à pergunta com mais precisão e sugerir um exemplo de uma função e um exemplo de seu uso.
E chame esta função
A saída é
Então tente executar a consulta com outra condição de grupo.
violino
Se no seu servidor não estiver disponível a função
IIF(...)
converta esta expressão usandocase when ... end
violino
e