How to show Convert rows to column with grand total - sql-server

Thank you in advance for assistance!
What i am trying to accomplish is this: I have a table with different salary grades in the same department. How can I display the Salary Grades in the Columns, with the count below for each columns, and then show the GRAND to of salary grades for each department. This is what i have:
Dept_Num Dept_Name Salary_Grade TOTAL_SalaryGrade
2005 Mrktg 1 39
2005 Mrktg 2 26
2005 Mrktg 3 5
2010 Payroll 1 20
2010 Payroll 2 8
2015 Acct 1 6
2015 Acct 3 6
Dept_Num Dept_Name Salary_Grade 1 Salary_Grade 2 Salary_Grade 3
2005 Mrktg 39 26 5
2010 Payroll 20 8 0
2015 Acct 6 0 6
TOTAL_SalaryGrade 65 34 11
SELECT[Dept_Num]
,[Dept_Name]
[Salary_Grades],
[TOTAL_Salary_Grades],
FROM [Employees]
Thank you for any assistance given!

Try as follows:
CREATE TABLE EMPLOYEES (Dept_Num INT, Dept_Name CHAR(20), Salary_Grade INT, TOTAL_SalaryGrade INT )
INSERT INTO EMPLOYEES VALUES (2005,'Mrktg' ,1,39 )
INSERT INTO EMPLOYEES VALUES (2005,'Mrktg' ,2,26 )
INSERT INTO EMPLOYEES VALUES (2005,'Mrktg' ,3, 5 )
INSERT INTO EMPLOYEES VALUES (2010,'Payroll',1,20 )
INSERT INTO EMPLOYEES VALUES (2010,'Payroll',2, 8 )
INSERT INTO EMPLOYEES VALUES (2015,'Acct' ,1, 6 )
INSERT INTO EMPLOYEES VALUES (2015,'Acct' ,3, 6 )
(SELECT convert(varchar,Dept_Num) [Dept_Num],Dept_Name, isnull([1],0) [Salary_Grade 1],isnull([2],0) [Salary_Grade 2],isnull([3],0) [Salary_Grade 3] from
(
select Dept_Num
, Dept_Name
, TOTAL_SalaryGrade
, Salary_Grade
from EMPLOYEES
) x
pivot
(
SUM(TOTAL_SalaryGrade)
for Salary_Grade in ([1],[2],[3])
) p )
union all
select 'TOTAL_SalaryGrade','',[1],[2],[3] from
(SELECT SUM (TOTAL_SalaryGrade) as total,Salary_Grade FROM EMPLOYEES GROUP BY Salary_Grade ) b
pivot
(sum(total) for Salary_Grade in ([1],[2],[3])) q
The result is:
*-----------------*----------*------------------*---------------*---------------*
|Dept_Num | Dept_Name| Salary_Grade 1 |Salary_Grade 2 |Salary_Grade 3|
*-----------------*----------*------------------*---------------*---------------
|2015 | Acct | 6 | 0 | 6 |
*-----------------*----------*------------------*---------------*---------------
|2005 | Mrktg | 39 | 26 | 5 |
*-----------------*----------*------------------*---------------*---------------
|2010 | Payroll | 20 | 8 | 0 |
*-----------------*----------*------------------*---------------*---------------
|TOTAL_SalaryGrade| | 65 | 34 |11 |
*-----------------*----------*------------------*---------------*---------------

Related

How can I take out each element of string to a separate column? [duplicate]

This question already has answers here:
How to split a comma-separated value to columns
(38 answers)
Closed 3 years ago.
I have table like:
|----|----------------|--------------------------|----------------------|
| id | tickets | comb1 | comb2 |
|---------------------|--------------------------|----------------------|
| 1 | 3146000011086..| ,13, ,31, ,50,66,77,..| ,22,38,40, , .. |
|---------------------|--------------------------|----------------------|
|2..n| 314600001924...| 5,14,23, , ,50, , ,..| 4,12,21, ,47, ,.. |
|-----------------------------------------------------------------------|
I need to take out each elements of comb1 and comb2 to columns like:
|---------------------|------------------|------------------|---------------|
| val_of_comb1(1) | val_of_comb1(2) | ..val_of_comb2(1)|val_of_comb2(2)|
|---------------------|------------------|------------------|---------------|
| | 13 | | 22 |
|---------------------|------------------|------------------|---------------|
| 5 | 14 | .. 4 | 12 |
|---------------------|------------------|------------------|---------------|
Maybe take out each element with loop? (but if I have a lot of records how it will affect the database) welcome any ideas
A. cross apply, pivot, and string_split
Here is a version if Comb1 splits into 12 strings.
drop table X
create table X
(
id int,
comb1 nvarchar(max)
);
insert into X values (1,',13, ,31, ,50,66,77,..');
insert into X values (2,'5,14,23, , ,50, , ,..');
-- From https://stackoverflow.com/questions/12195504/splitting-a-string-then-pivoting-result by Kannan Kandasamy
select * from (
select * from X x cross apply (select RowN=Row_Number() over (Order by (SELECT NULL)), value from string_split(x.Comb1, ',') ) d) src
pivot (max(value) for src.RowN in([1],[2],[3],[4],[5],[6],[7],[8],[9],[10],[11],[12])) as p
id comb1 1 2 3 4 5 6 7 8 9 10 11 12
1 ,13, ,31, ,50,66,77,.. 13 31 50 66 77 .. NULL NULL NULL
2 5,14,23, , ,50, , ,.. 5 14 23 50 .. NULL NULL NULL
B. Just STRING_SPLIT and code
One option is to use STRING_SPLIT which will return rows.
select value from STRING_SPLIT(',13, ,31, ,50,66,77,..',',');
value
13
31
50
66
77
..
You could then collect all the rows in your code and collect them as an array.

Calculate row difference within groups

I'm looking for help with calculating the difference between consecutive ordered rows within groups in SQL (Microsoft SQL server).
I have a table like this:
ID School_ID Enrollment_Start_Date Order
1 56 1/1/2018 10
1 56 5/5/2018 24
1 56 7/7/2018 35
1 103 4/4/2019 26
1 103 3/3/2019 19
I want to calculate the difference between Order, group by ID, School_ID, and order by Enrollment_Start_Date.
so I want something like this:
ID School_ID Enrollment_Start_Date Order Diff
1 56 1/1/2018 10 10 # nothing to be subtracted from 10
1 56 5/5/2018 24 14 # 24-10
1 56 7/7/2018 35 11 # 35-24
1 103 3/3/2019 19 19 # nothing to be subtracted from 19
1 103 4/4/2019 26 7 # 26-19
I have hundreds of IDs, and each ID can have at most 6 Enrollment_Start_Date, so I'm looking for some generalizable implementations.
Use LAG(<column>) analytic function to obtain a "previous" column value specified within the OVER part, then substract current value from it and make it a positive number multiplying it by -1. If previous value isn't present (is null) then take the current value.
Pseudo code would be:
If previous_order_value exists:
-1 * (previous_order_value - current_order_value)
Else
current_order_value
where previous_order_value is based on the same id & school_id and is sorted by enrollment_start_date in ascending order
SQL Code:
select
id,
school_id,
enrollment_start_date,
[order],
coalesce(-1 * (lag([order]) over (partition by id, school_id order by enrollment_start_date ) - [order]), [order]) as diff
from yourtable
Also note, that order keyword is reserved in SQL Server, which is why your column was created with name wrapped within [ ]. I suggest using some other word for this column, if possible.
use lag() analytic function for getting difference of two row and case when for getting orginal value of order column where no difference exist
with cte as
(
select 1 as id, 56 as sclid, '2018-01-01' as s_date, 10 as orders
union all
select 1,56,'2018-05-05',24 union all
select 1,56,'2018-07-07',35 union all
select 1,103,'2019-04-04',26 union all
select 1,103,'2019-03-03',19
) select t.*,
case when ( lag([orders])over(partition by id,sclid order by s_date ) -[orders] )
is null then [orders] else
( lag([orders])over(partition by id,sclid order by s_date ) -[orders] )*(-1) end
as diff
from cte t
output
id sclid s_date orders diff
1 56 2018-01-01 10 10
1 56 2018-05-05 24 14
1 56 2018-07-07 35 11
1 103 2019-03-03 19 19
1 103 2019-04-04 26 7
demo link
Use LAG(COLUMN_NAME)
Query
SELECT id, School_ID, Enrollment_Start_Date, cOrder,
ISNULL((cOrder - (LAG(cOrder) OVER(PARTITION BY id, School_ID ORDER BY Enrollment_Start_Date))),cOrder)Diff
FROM Table1
Samle Output
| id | School_ID | Enrollment_Start_Date | cOrder | Diff |
|----|-----------|-----------------------|--------|------|
| 1 | 56 | 2018-01-01 | 10 | 10 |
| 1 | 56 | 2018-05-05 | 24 | 14 |
| 1 | 56 | 2018-07-07 | 35 | 11 |
| 1 | 103 | 2019-03-03 | 19 | 19 |
| 1 | 103 | 2019-04-04 | 26 | 7 |
SQL Fiddle Demo

Using a condition for cross join on months

Table 1 has column date , value
Table 2 has monthnumber , monthname (ie 1-12 for number and Jan - Dec)
Sample Data
|Table 1|
|ColDate | value|
|1-nov-2016 | 6|
Expected Output
ColDate | value | month | monthnumber
1-nov-2016 | 6 | Nov | 11
1-nov-2016 | 0 | Dec |12
..... 0 for all other months except Nov
I used cross join between table 1 and table 2 but it gives output as
1-nov-2016 | 6 | Nov | 11
1-nov-2016 | 6 | Dec |12
..... 6 for all other months though should be 0 except Nov.
How do i do that ?
Try this,
DECLARE #TB1 TABLE (COLDATE VARCHAR(20),VALUE INT)
INSERT INTO #TB1
SELECT '1-NOV-2016',6
DECLARE #TB2 TABLE(MONTH VARCHAR(10),MONTHNUMBER INT)
INSERT INTO #TB2
SELECT 'NOV',11
UNION ALL
SELECT 'DEC',12
SELECT COLDATE
,CASE WHEN MONTHNUMBER=11 THEN VALUE ELSE 0 END VALUE
,MONTH
,MONTHNUMBER
FROM #TB1
CROSS JOIN #TB2
Result:

How to find first positive value and third consecutive positive values in SQL?

I have tried using ROW_NUMBER but haven't quite got it. Any ideas on the best way to achieve this.
I am looking to find:
- What month did they first cash flow.
- What month did they average 3 months cash flow.
Sample Data:
Office ,Balance & Year month
------------------------------
| Office | Balance | YrMo |
| 12 | 111 | 201510 |
| 12 | 222 | 201511 |
| 12 | -444 | 201512 |
| 12 | -777 | 201601 |
| 12 | 555 | 201602 |
| 12 | 666 | 201603 |
| 12 | -888 | 201604 |
| 12 | 777 | 201605 |
| 40 | -555 | 201510 |
| 40 | -200 | 201511 |
| 40 | 0 | 201512 |
| 40 | 100 | 201601 |
| 40 | -555 | 201602 |
| 40 | 666 | 201603 |
| 40 | 777 | 201604 |
| 40 | 888 | 201605 |
| 40 | 999 | 201606 |
The first Positive Balances would be:
-office 12 , Balance 111 , YrMo 201510
-office 40 , Balance 100 , YrMo 201601
The first month the office averaged 3 positive balance:
-office 40 , Balance 999 , YrMo 201606
Here is the #test table script:
IF OBJECT_ID('tempdb..#test') IS NOT NULL
DROP TABLE #test
GO
CREATE TABLE #test (office INT , Balance INT, YrMo INT ) ;
INSERT INTO #test VALUES (12 , 111 , 201510) ;
INSERT INTO #test VALUES (12 , 222 , 201511) ;
INSERT INTO #test VALUES (12 , -444 , 201512) ;
INSERT INTO #test VALUES (12 , -777 , 201601) ;
INSERT INTO #test VALUES (12 , 555 , 201602) ;
INSERT INTO #test VALUES (12 , 666 , 201603) ;
INSERT INTO #test VALUES (12 , -888 , 201604) ;
INSERT INTO #test VALUES (12 , 777 , 201605) ;
INSERT INTO #test VALUES (40 , -555 , 201510) ;
INSERT INTO #test VALUES (40 , -200 , 201511) ;
INSERT INTO #test VALUES (40 , 0 , 201512) ;
INSERT INTO #test VALUES (40 , 100 , 201601) ;
INSERT INTO #test VALUES (40 , -555 , 201602) ;
INSERT INTO #test VALUES (40 , 666 , 201603) ;
INSERT INTO #test VALUES (40, 777 , 201604) ;
INSERT INTO #test VALUES (40 , 888 , 201605) ;
INSERT INTO #test VALUES (40 , 999 , 201606) ;
Thanks in advance
;with cteFirst as (
Select *
,FirstPos=Row_Number() over (Partition By Office Order By YrMo,Balance) from #Test Where Balance>0
),
cteCons as (
Select *
,TestCons=Lag(IIf(IIf(sign(balance)=1,1,0)=1,1,0),1,0) over (Partition By Office Order By YrMo)
+Lag(IIf(IIf(sign(balance)=1,1,0)=1,1,0),2,0) over (Partition By Office Order By YrMo)
+Lag(IIf(IIf(sign(balance)=1,1,0)=1,1,0),3,0) over (Partition By Office Order By YrMo)
from #Test
)
Select *,Status='First Positive' from cteFirst where FirstPos=1
Union All
Select *,Status='3 Cons' from cteCons where TestCons=3
Return
office Balance YrMo FirstPos Status
12 111 201510 1 First Positive
40 100 201601 1 First Positive
40 999 201606 3 3 Consequtive
I added another example. This one traps gaps in Dates.
If you want to see all the flags and how the data progresses, remove
the -- before Select * from cteFinal Order by Office,YrMo
I added another office which has 3 consecutive positive balances, but the months are NOT (no June). Notice Office 99 fails to meet the consecutive months criteria
office Balance YrMo
99 199 201605
99 299 201607
99 399 201608
The updated query is as follows
;with cteBase as (
Select *
,RowNr = Row_Number() over (Partition By Office Order By Office,YrMo,Balance)
,MthSeq = case when cast(YrMo as int)-Lag(YrMo,1,YrMo-1) over (Partition By Office Order By YrMo) in (1,89) then 1 else 0 end
,IsPos = IIf(Balance>0,1,null)
from #Test
)
,cteFinal as (
Select *
,PosRowNr = min(RowNr*IsPos) over (Partition By Office Order By RowNr)
,TestCons = MthSeq * (
Lag(IIf(IIf(sign(balance)=1,1,0)=1,1,0),1,0) over (Partition By Office Order By YrMo)
+Lag(IIf(IIf(sign(balance)=1,1,0)=1,1,0),2,0) over (Partition By Office Order By YrMo)
+Lag(IIf(IIf(sign(balance)=1,1,0)=1,1,0),3,0) over (Partition By Office Order By YrMo)
)
From cteBase
)
--Select * from cteFinal Order by Office,YrMo
Select Office
,Balance
,YrMo
,Status = IIf(RowNr=PosRowNr,'First Positive','')+IIf(TestCons=3,'Consecutive Months','')
From cteFinal
Where TestCons=3 or RowNr=PosRowNr
Order by Status Desc,Office,YrMo
The Results are
Office Balance YrMo Status
12 111 201510 First Positive
40 100 201601 First Positive
99 199 201605 First Positive
40 999 201606 Consecutive Months

LAG of MIN in SQL Analytic

I have a table containing employees id, year id, client id, and the number of sales. For example:
--------------------------------------
id_emp | id_year | sales | client id
--------------------------------------
4 | 1 | 14 | 1
4 | 1 | 10 | 2
4 | 2 | 11 | 1
4 | 2 | 17 | 2
For a employee, I want to obtain rows with the minimum sales per year and the minimum sales of the previous year.
One of the queries I tried is the following:
select distinct
id_emp,
id_year,
MIN(sales) OVER(partition by id_emp, id_year) AS min_sales,
LAG(min(sales), 1) OVER(PARTITION BY id_emp, id_year
ORDER BY id_emp, id_year) AS previous
from facts
where id_emp = 4
group by id_emp, id_year, sales;
I get the result:
-------------------------------------
id_emp | id_year | sales | previous
-------------------------------------
4 | 1 | 10 | (null)
4 | 1 | 10 | 10
4 | 2 | 11 | (null)
but I expect to get:
-------------------------------------
id_emp | id_year | sales | previous
-------------------------------------
4 | 1 | 10 | (null)
4 | 2 | 11 | 10
SQL Fiddle
Oracle 11g R2 Schema Setup:
CREATE TABLE EMPLOYEE_SALES ( id_emp, id_year, sales, client_id ) AS
SELECT 4, 1, 14, 1 FROM DUAL
UNION ALL SELECT 4, 1, 10, 2 FROM DUAL
UNION ALL SELECT 4, 2, 11, 1 FROM DUAL
UNION ALL SELECT 4, 2, 17, 2 FROM DUAL;
Query 1:
SELECT ID_EMP,
ID_YEAR,
SALES AS SALES,
LAG( SALES ) OVER ( PARTITION BY ID_EMP ORDER BY ID_YEAR ) AS PREVIOUS
FROM (
SELECT e.*,
ROW_NUMBER() OVER ( PARTITION BY id_emp, id_year ORDER BY sales ) AS RN
FROM EMPLOYEE_SALES e
)
WHERE rn = 1
Query 2:
SELECT ID_EMP,
ID_YEAR,
MIN( SALES ) AS SALES,
LAG( MIN( SALES ) ) OVER ( PARTITION BY ID_EMP ORDER BY ID_YEAR ) AS PREVIOUS
FROM EMPLOYEE_SALES
GROUP BY ID_EMP, ID_YEAR
Results - Both give the same output:
| ID_EMP | ID_YEAR | SALES | PREVIOUS |
|--------|---------|-------|----------|
| 4 | 1 | 10 | (null) |
| 4 | 2 | 11 | 10 |
You mean like this?
select id_emp, id_year, min(sales) as min_sales,
lag(min(sales)) over (partition by id_emp order by id_year) as prev_year_min_sales
from facts
where id_emp = 4
group by id_emp, id_year;
I believe it is because you are using sales column in your group by statement.
Try to remove it and just use
GROUP BY id_emp,id_year
You could get your desired output using ROW_NUMBER() and LAG() analytic functions.
For example,
Table
SQL> SELECT * FROM t;
ID_EMP ID_YEAR SALES CLIENT_ID
---------- ---------- ---------- ----------
4 1 14 1
4 1 10 2
4 2 11 1
4 2 17 2
Query
SQL> WITH DATA AS
2 (SELECT t.*,
3 row_number() OVER(PARTITION BY id_emp, id_year ORDER BY sales) rn
4 FROM t
5 )
6 SELECT id_emp,
7 id_year ,
8 sales ,
9 lag(sales) over(order by sales) previous
10 FROM DATA
11 WHERE rn =1;
ID_EMP ID_YEAR SALES PREVIOUS
---------- ---------- ---------- ----------
4 1 10
4 2 11 10

Resources