Complex Grouping in Sql with two or three Group criteria for single table and Joins - sql-server

This is my table in ms-sql 2008
Id ReceiptNo StudId Year SchoolId ClassId FeeId Paid Balance
18 1 22 1 4 1 8 50 100
19 1 22 1 4 1 9 100 2300
20 2 23 1 5 1 9 200 2200
21 2 23 1 5 1 10 100 900
22 3 22 1 4 1 8 100 0
23 3 22 1 4 1 9 100 2200
I want rows where Balance is minimum but not 0, of that specific StudId and FeeId. Actually I want to show pending Fees to User, meaning, this Student has this much balance remaining of this fee type for this Academic Year. If you observe row with Id 18 has StudId 22 and FeeId 8 and Balance 100, now row with Id 22 shows Balance 0 with same student and same fee, meaning this 22 student has paid all fees of 8 FeeId, so I don’t want this student for FeeId 8. Now the same student has paid fee with FeeId 9, row with Id 19 & 23 and has two Balance 2300 and 2200, so I want to show that StudId 22 has Balance of 2200 remaining for FeeId 9, meaning I want to show row with Id 23 and NOT the row with Id 19. Now this applies to all students for whatever Fee Id they have.
I have tried this but not getting
SELECT StudentId,FeeTypeId,MIN(FeesBalance)
FROM FeesCollectionRS
WHERE FeesBalance !=0 GROUP BY StudentId,FeeTypeId
Actually in reality my query will be some what like this
SELECT f.StudentId,f.AcademicYear,f.SchoolId,f.MstClassId,f.FeeTypeId,d.PrnNo ,dbo.GetStudNameByStudid(f.StudentId) student,
CAST(year(a.StartOfYear)as CHAR(4))+'-'+ CAST(year(a.EndOfYear)as CHAR(4)) [Academic Year],c.Name,m.FeeType,f.FeesBalance
FROM
FeesCollectionRS f JOIN AcademicYearMasterRS a
ON f.AcademicYear = a.Id JOIN ClassMasterRS c
ON f.MstClassId = c.MstClassId JOIN MstFeeType m
ON f.FeeTypeId = m.FeeTypeId JOIN MstStudentRS d
ON d.StudentId = f.StudentId
WHERE
f.AcademicYear =1 and f.FeesBalance !=0

Use a HAVING clause to filter out minimums that are zero:
SELECT StudentId, FeeTypeId, MIN(FeesBalance) MinFeesBalance
FROM FeesCollectionRS
GROUP BY StudentId, FeeTypeId
HAVING MIN(FeesBalance) > 0
This first groups to find the minimum, then returns only those groups that have the criteria in HAVING, which is like a WHERE equivalent for groups and is applied to the result of aggregations rather than column values.

How about this:
SQL Fiddle
MS SQL Server 2008 Schema Setup:
CREATE TABLE StudentFees
(
Id INT PRIMARY KEY,
ReceiptNo INT,
StudId INT,
[Year] SMALLINT,
SchoolId INT,
ClassId INT,
FeeId INT,
Paid INT,
Balance INT
)
INSERT INTO StudentFees
VALUES (18, 1, 22, 1, 4, 1, 8, 50, 100),
(19, 1, 22, 1, 4, 1, 9, 100, 2300),
(20, 2, 23, 1, 5, 1, 9, 200, 2200),
(21, 2, 23, 1, 5, 1, 10, 100, 900),
(22, 3, 22, 1, 4, 1, 8, 100, 0),
(23, 3, 22, 1, 4, 1, 9, 100, 2200)
Query 1:
;WITH CTE
AS
(
SELECT StudId, FeeId, [Year], MAX(Id) As MaxId
FROM StudentFees
GROUP BY StudId, FeeId, [Year]
)
SELECT SF.StudId, SF.[Year],
SF.SchoolId, SF.ClassId, SF.FeeId, SF.Balance
FROM StudentFees SF
INNER JOIN CTE
ON CTE.MaxId = SF.Id
WHERE SF.Balance > 0
Results:
| StudId | Year | SchoolId | ClassId | FeeId | Balance |
|--------|------|----------|---------|-------|---------|
| 22 | 1 | 4 | 1 | 9 | 2200 |
| 23 | 1 | 5 | 1 | 9 | 2200 |
| 23 | 1 | 5 | 1 | 10 | 900 |

Related

Unpivot Multiple Paired columns [duplicate]

I am using SQL server 2008 and I am trying to unpivot the data. Here is the SQL code that I am using,
CREATE TABLE #pvt1 (VendorID int, Sa int, Emp1 int,Sa1 int,Emp2 int)
GO
INSERT INTO #pvt1 VALUES (1,2,4,3,9);
GO
--Unpivot the table.
SELECT distinct VendorID,Orders,Orders1
FROM
(SELECT VendorID, Emp1, Sa,Emp2,Sa1
FROM #pvt1 ) p
UNPIVOT
(Orders FOR Emp IN
(Emp1,Emp2)
)AS unpvt
UNPIVOT
(Orders1 FOR Emp1 IN
(Sa,Sa1)
)AS unpvt1;
GO
And Here is the result of the above code.
VendorID Orders Orders1
1 4 2
1 4 3
1 9 2
1 9 3
But I want my Output to be the way indicated below
VendorID Orders Orders1
1 4 2
1 9 3
The relationship from the above code is 2 is related to 4, and 3 is related to 9.
How can I achieve this?
An easier way to unpivot the data would be to use a CROSS APPLY to unpivot the columns in pairs:
select vendorid, orders, orders1
from pvt1
cross apply
(
select emp1, sa union all
select emp2, sa1
) c (orders, orders1);
See SQL Fiddle with Demo. Or you can use CROSS APPLY with the VALUES clause if you don't want to use the UNION ALL:
select vendorid, orders, orders1
from pvt1
cross apply
(
values
(emp1, sa),
(emp2, sa1)
) c (orders, orders1);
See SQL Fiddle with Demo
The answer by Taryn is indeed super useful, and I'd like to expand one aspect of it.
If you have a very un-normalized table like this, with multiple sets of columns for e.g. 4 quarters or 12 months:
+-------+------+------+------+------+------+------+-------+------+
| cYear | foo1 | foo2 | foo3 | foo4 | bar1 | bar2 | bar3 | bar4 |
+-------+------+------+------+------+------+------+-------+------+
| 2020 | 42 | 888 | 0 | 33 | one | two | three | four |
+-------+------+------+------+------+------+------+-------+------+
Then the CROSS APPLY method is easy to write and understand, when you got the hang of it. For the numbered column, use constant values.
SELECT
cYear,
cQuarter,
foo,
bar
FROM temp
CROSS APPLY
(
VALUES
(1, foo1, bar1),
(2, foo2, bar2),
(3, foo3, bar3),
(4, foo4, bar4)
) c (cQuarter, foo, bar)
Result:
+-------+----------+-----+-------+
| cYear | cQuarter | foo | bar |
+-------+----------+-----+-------+
| 2020 | 1 | 42 | one |
| 2020 | 2 | 888 | two |
| 2020 | 3 | 0 | three |
| 2020 | 4 | 33 | four |
+-------+----------+-----+-------+
SQL Fiddle
I needed composit key AND skip extras row in case when data is missing (NULLs). For ex. when x2 and y2 are possible replacement vendor and price
WITH pvt AS (SELECT * FROM (VALUES
( 1, 6, 11, 111, 12, 13, 122, 133),
( 2, 6, 21, 211, 22, 23, 222, 233),
( 3, 6, 31, 311, 32, 33, 322, 333),
( 5, 4, 41, 411, 42, NULL, 422, NULL),
( 6, 4, 51, 511, 52, NULL, 522, NULL))
s( id, s, a, b, x1, x2, y1, y2)
)
-- SELECT * FROM pvt
SELECT CONCAT('xy_',s,'_', id, postfix) as comp_id, a, b, x, y
FROM pvt
CROSS APPLY
(
VALUES
(NULL, x1, y1),
('_ext', x2, y2)
) c (postfix, x, y)
WHERE x IS NOT NULL
produces
comp_id a b x y
-------------------------------- ----------- ----------- ----------- -----------
xy_6_1 11 111 12 122
xy_6_1_ext 11 111 13 133
xy_6_2 21 211 22 222
xy_6_2_ext 21 211 23 233
xy_6_3 31 311 32 322
xy_6_3_ext 31 311 33 333
xy_4_5 41 411 42 422
xy_4_6 51 511 52 522
(8 rows affected)
from:
id s a b x1 x2 y1 y2
----------- ----------- ----------- ----------- ----------- ----------- ----------- -----------
1 6 11 111 12 13 122 133
2 6 21 211 22 23 222 233
3 6 31 311 32 33 322 333
5 4 41 411 42 NULL 422 NULL
6 4 51 511 52 NULL 522 NULL
(5 rows affected)

“Stuff and 'For Xml Path'” or UNION to exclude duplicate rows

This is further progress of question here: how to use "Stuff and 'For Xml Path'" to unite rows in table
I have 3 tables - invoices, positions and accounts which build common table by SELECT as specified in result below. I need to exclude duplicate rows which appear due to different accounts for same invoices. I think it can be solved by “Stuff and 'For Xml Path'” and/or by UNION, but I don't really know how to do that.
Please assist with this issue. Thank You in advance.
Here's dbfiddle with DL and sample data population: https://dbfiddle.uk/?rdbms=sqlserver_2019&fiddle=6401c2886a24b21239dade27e8c549ec
Using DevExpress dashboard designer based on SQL Server 2016.
Query:
-- DDL and sample data population, start
CREATE TABLE invoices
(
invoice VARCHAR(20) NOT NULL,
id INT NOT NULL,
);
INSERT invoices(invoice, id)
VALUES ('ty20210110', 1);
INSERT invoices(invoice, id)
VALUES ('ty20210111', 2);
INSERT invoices(invoice, id)
VALUES ('ty20210112', 3);
CREATE TABLE positions
(
position VARCHAR(20) NOT NULL,
quantity INT NOT NULL,
price INT NOT NULL,
summ INT NOT NULL,
invoice INT NOT NULL,
);
INSERT positions(position, quantity, price, summ, invoice)
VALUES ('p1000001', 2, 100, 200, 1);
INSERT positions(position, quantity, price, summ, invoice)
VALUES ('p1000002', 3, 100, 300, 2);
INSERT positions(position, quantity, price, summ, invoice)
VALUES ('p1000003', 1, 250, 250, 2);
INSERT positions(position, quantity, price, summ, invoice)
VALUES ('p1000004', 2, 120, 240, 1);
INSERT positions(position, quantity, price, summ, invoice)
VALUES ('p1000005', 4, 100, 400, 1);
INSERT positions(position, quantity, price, summ, invoice)
VALUES ('p1000006', 3, 100, 300, 1001);
INSERT positions(position, quantity, price, summ, invoice)
VALUES ('p1000007', 5, 80, 400, 3);
INSERT positions(position, quantity, price, summ, invoice)
VALUES ('p1000008', 5, 100, 500, 3);
CREATE TABLE accounts
(
account INT NOT NULL,
invoice INT NOT NULL,
);
INSERT accounts(account, invoice)
VALUES (01, 2);
INSERT accounts(account, invoice)
VALUES (02, 2);
INSERT accounts(account, invoice)
VALUES (03, 1);
INSERT accounts(account, invoice)
VALUES (04, 3);
INSERT accounts(account, invoice)
VALUES (05, 1);
INSERT accounts(account, invoice)
VALUES (06, 3);
-- DDL and sample data population, end
SELECT
positions.position, positions.quantity, positions.price, positions.summ,
invoices.invoice,
accounts.account
FROM
positions
INNER JOIN
invoices invoices ON invoices.id = positions.invoice
INNER JOIN
accounts accounts ON invoices.id = accounts.invoice
Result:
position
quantity
price
summ
invoice
account
p1000001
2
100
200
in20210110
3
p1000001
2
100
200
in20210110
5
p1000002
3
100
300
in20210111
1
p1000002
3
100
300
in20210111
2
p1000003
1
250
250
in20210111
1
p1000003
1
250
250
in20210111
2
p1000004
2
120
240
in20210110
3
p1000004
2
120
240
in20210110
5
p1000005
4
100
400
in20210110
3
p1000005
4
100
400
in20210110
5
p1000006
3
100
300
in20210110
3
p1000006
3
100
300
in20210110
5
p1000007
5
80
400
in20210112
4
p1000007
5
80
400
in20210112
6
p1000008
5
100
500
in20210112
4
p1000008
5
100
500
in20210112
6
Required result 1:
position
quantity
price
summ
invoice
account
p1000001
2
100
200
in20210110
3, 5
p1000004
2
120
240
in20210110
3, 5
p1000005
4
100
400
in20210110
3, 5
p1000006
3
100
300
in20210110
3, 5
p1000002
3
100
300
in20210111
1, 2
p1000003
1
250
250
in20210111
1, 2
p1000007
5
80
400
in20210112
4, 6
p1000008
5
100
500
in20210112
4, 6
Required result 2:
position
quantity
price
summ
invoice
account 1
account 2
p1000001
2
100
200
in20210110
3
5
p1000004
2
120
240
in20210110
3
5
p1000005
4
100
400
in20210110
3
5
p1000006
3
100
300
in20210110
3
5
p1000002
3
100
300
in20210111
1
2
p1000003
1
250
250
in20210111
1
2
p1000007
5
80
400
in20210112
4
6
p1000008
5
100
500
in20210112
4
6
Your first resultset can be handled in two ways, depending on your version of SQL Server. Try the following examples in SSMS:
Create Data
DECLARE #invoices table (
invoice VARCHAR(20) NOT NULL, id INT NOT NULL
);
INSERT INTO #invoices (invoice, id) VALUES
( 'ty20210110', 1 ), ( 'ty20210111', 2 ), ( 'ty20210112', 3 );
DECLARE #positions table (
position VARCHAR(20) NOT NULL, quantity INT NOT NULL, price INT NOT NULL, summ INT NOT NULL, invoice INT NOT NULL
);
INSERT INTO #positions ( position, quantity, price, summ, invoice ) VALUES
( 'p1000001', 2, 100, 200, 1 ),
( 'p1000002', 3, 100, 300, 2 ),
( 'p1000003', 1, 250, 250, 2 ),
( 'p1000004', 2, 120, 240, 1 ),
( 'p1000005', 4, 100, 400, 1 ),
( 'p1000006', 3, 100, 300, 1 ),
( 'p1000007', 5, 80, 400, 3 ),
( 'p1000008', 5, 100, 500, 3 );
DECLARE #accounts table (
account INT NOT NULL, invoice INT NOT NULL
);
INSERT INTO #accounts ( account, invoice ) VALUES
( 1, 2 ), ( 2, 2 ), ( 3, 1 ), ( 4, 3 ), ( 5, 1 ), ( 6, 3 );
If you're using SQL Server 2017+
SELECT
positions.position,
positions.quantity,
positions.price,
positions.summ,
invoices.invoice,
STRING_AGG ( accounts.account, ',' ) AS account
FROM #positions positions
INNER JOIN #invoices invoices
ON invoices.id = positions.invoice
INNER JOIN #accounts accounts
ON invoices.id = accounts.invoice
GROUP BY
position, quantity, price, summ, invoices.invoice
ORDER BY
invoice;
For Pre-SQL Server 2017
SELECT
positions.position,
positions.quantity,
positions.price,
positions.summ,
invoices.invoice,
acct.account
FROM #positions positions
INNER JOIN #invoices invoices
ON invoices.id = positions.invoice
INNER JOIN #accounts accounts
ON invoices.id = accounts.invoice
OUTER APPLY (
SELECT STUFF ( (
SELECT ',' + CAST ( a.account AS varchar(50) ) AS "text()"
FROM #accounts a
WHERE
a.invoice = invoices.id
FOR XML PATH ( '' )
), 1, 1, '' ) AS account
) AS acct
GROUP BY
position, quantity, price, summ, invoices.invoice, acct.account
ORDER BY
invoice;
Both queries return
+----------+----------+-------+------+------------+---------+
| position | quantity | price | summ | invoice | account |
+----------+----------+-------+------+------------+---------+
| p1000001 | 2 | 100 | 200 | ty20210110 | 3,5 |
| p1000004 | 2 | 120 | 240 | ty20210110 | 3,5 |
| p1000005 | 4 | 100 | 400 | ty20210110 | 3,5 |
| p1000006 | 3 | 100 | 300 | ty20210110 | 3,5 |
| p1000002 | 3 | 100 | 300 | ty20210111 | 1,2 |
| p1000003 | 1 | 250 | 250 | ty20210111 | 1,2 |
| p1000007 | 5 | 80 | 400 | ty20210112 | 4,6 |
| p1000008 | 5 | 100 | 500 | ty20210112 | 4,6 |
+----------+----------+-------+------+------------+---------+
The second resultset you requested gets considerably more complicated and requires the use of the XML datatype. The following example makes liberal assumptions of your data, most notably that only two accounts are expected. Having said that, you can expand the [account n] columns in the PIVOT as necessary without having to resort to dynamic SQL.
SELECT DISTINCT
positions.position,
positions.quantity,
positions.price,
positions.summ,
invoices.invoice
, x.*
FROM #positions positions
INNER JOIN #invoices invoices
ON invoices.id = positions.invoice
INNER JOIN #accounts accounts
ON invoices.id = accounts.invoice
OUTER APPLY (
-- Create an XML account list --
SELECT CAST ( (
SELECT
'account ' + CAST ( ROW_NUMBER() OVER ( ORDER BY a.invoice ) AS varchar(50) ) AS id,
a.account
FROM #accounts a
WHERE
a.invoice = invoices.id
FOR XML PATH ( 'data' ), ROOT ( 'accounts' )
) AS xml ) AS account_xml
) AS acct
OUTER APPLY (
-- PIVOT the account details --
SELECT
*
FROM (
SELECT
x.f.value( 'id[1]', 'varchar(50)' ) AS id,
x.f.value( 'account[1]', 'varchar(50)' ) AS act
FROM acct.account_xml.nodes( '//accounts/data' ) x(f)
) AS d
PIVOT (
MAX ( act ) FOR id IN ( [account 1], [account 2] )
) AS p
) AS x
ORDER BY
invoice;
Returns
+----------+----------+-------+------+------------+-----------+-----------+
| position | quantity | price | summ | invoice | account 1 | account 2 |
+----------+----------+-------+------+------------+-----------+-----------+
| p1000001 | 2 | 100 | 200 | ty20210110 | 3 | 5 |
| p1000004 | 2 | 120 | 240 | ty20210110 | 3 | 5 |
| p1000005 | 4 | 100 | 400 | ty20210110 | 3 | 5 |
| p1000006 | 3 | 100 | 300 | ty20210110 | 3 | 5 |
| p1000002 | 3 | 100 | 300 | ty20210111 | 1 | 2 |
| p1000003 | 1 | 250 | 250 | ty20210111 | 1 | 2 |
| p1000007 | 5 | 80 | 400 | ty20210112 | 4 | 6 |
| p1000008 | 5 | 100 | 500 | ty20210112 | 4 | 6 |
+----------+----------+-------+------+------------+-----------+-----------+

How to show Convert rows to column with grand total

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 |
*-----------------*----------*------------------*---------------*---------------

Better way to do this recursive query for second oldest result

This works fine but I copied & pasted the segment from the other SO post as noted in the code comments. Is there a better way to do this?
SQL Fiddle
MS SQL Server 2008 Schema Setup:
CREATE TABLE GoalType ([GoalTypeID] int, [Type] varchar(25));
INSERT INTO GoalType ([GoalTypeID], [Type]) VALUES
(1, 'Price'),
(2, 'Revenue'),
(3, 'Type2 Revenue'),
(4, 'Graduation Percentage'),
(5, 'Retention Percentage')
;
CREATE TABLE Goal ([GoalID] int, [GoalTypeID] int, [GymID] int, [Month] int, [Year] int, [Goal] int, [Created] datetime, [CreatedBy] int);
INSERT INTO Goal ([GoalID], [GoalTypeID], [GymID], [Month], [Year], [Goal], [Created], [CreatedBy]) VALUES
(1, 1, 8, 10, 2014, 3500, '2014-10-14 11:09:03', 12345),
(2, 1, 8, 10, 2014, 2500, '2014-10-14 11:09:04', 12345),
(3, 1, 8, 10, 2014, 1500, '2014-10-14 11:09:05', 12345),
(4, 1, 8, 9, 2014, 3500, '2014-10-14 11:09:06', 12345),
(5, 1, 8, 9, 2014, 2500, '2014-10-14 11:09:07', 12345),
(6, 1, 8, 9, 2014, 1500, '2014-10-14 11:09:08', 12345),
(7, 1, 4, 10, 2014, 3500, '2014-10-14 11:09:09', 12345),
(8, 1, 4, 10, 2014, 2500, '2014-10-14 11:09:10', 12345),
(9, 1, 4, 10, 2014, 1500, '2014-10-14 11:09:11', 12345),
(10, 2, 4, 10, 2014, 2500, '2014-10-14 11:09:12', 12345),
(11, 3, 4, 10, 2014, 1500, '2014-10-14 11:09:13', 12345)
;
Query 1:
--CREATE View ViewGoal AS
Select
m2.GoalID,
m2.GoalTypeID,
b.Type,
m2.GymID,
m2.Month,
m2.Year,
m2.Goal,
m2.Created,
m2.CreatedBy,
m3.GoalID As LastGoalID,
m3.Goal As LastValue,
m3.Created AS LastSet,
m3.CreatedBy as LastSetBy
FROM
(
SELECT m.*,row_number() over (
partition by GoalTypeID,GymID,Month,Year
order by Created desc,GymID,Month,Year) as rn
From Goal m
) m2 -- thanks https://stackoverflow.com/a/1049835/121668
JOIN
GoalType b
ON m2.GoalTypeID = b.GoalTypeID
LEFT JOIN
(
SELECT m.*,row_number() over (
partition by GoalTypeID,GymID,Month,Year
order by Created desc,GymID,Month,Year) as rn
From Goal m
) m3
ON m2.rn+1 = m3.rn
AND m2.GoalTypeID = m3.GoalTypeID
AND m2.GymID = m3.GymID
AND m2.Month = m3.Month
AND m2.Year = m3.Year
WHERE m2.rn=1
Results:
| GOALID | GOALTYPEID | TYPE | GYMID | MONTH | YEAR | GOAL | CREATED | CREATEDBY | LASTGOALID | LASTVALUE | LASTSET | LASTSETBY |
|--------|------------|---------------|-------|-------|------|------|--------------------------------|-----------|------------|-----------|--------------------------------|-----------|
| 9 | 1 | Price | 4 | 10 | 2014 | 1500 | October, 14 2014 11:09:11+0000 | 12345 | 8 | 2500 | October, 14 2014 11:09:10+0000 | 12345 |
| 6 | 1 | Price | 8 | 9 | 2014 | 1500 | October, 14 2014 11:09:08+0000 | 12345 | 5 | 2500 | October, 14 2014 11:09:07+0000 | 12345 |
| 3 | 1 | Price | 8 | 10 | 2014 | 1500 | October, 14 2014 11:09:05+0000 | 12345 | 2 | 2500 | October, 14 2014 11:09:04+0000 | 12345 |
| 11 | 3 | Type2 Revenue | 4 | 10 | 2014 | 1500 | October, 14 2014 11:09:13+0000 | 12345 | (null) | (null) | (null) | (null) |
| 10 | 2 | Revenue | 4 | 10 | 2014 | 2500 | October, 14 2014 11:09:12+0000 | 12345 | (null) | (null) | (null) | (null) |
Try this method using LEAD.
;WITH CTE AS
(
SELECT M.*,ROW_NUMBER() OVER (
PARTITION BY GOALTYPEID,GYMID,MONTH,YEAR
ORDER BY CREATED DESC,GYMID,MONTH,YEAR) AS RN,
LEAD(GOALID) OVER(PARTITION BY GOALTYPEID,GYMID,MONTH,YEAR
ORDER BY CREATED DESC,GYMID,MONTH,YEAR )AS LASTGOALID,
LEAD (GOAL) OVER(PARTITION BY GOALTYPEID,GYMID,MONTH,YEAR
ORDER BY CREATED DESC,GYMID,MONTH,YEAR )AS LASTVALUE,
LEAD (CREATED) OVER(PARTITION BY GOALTYPEID,GYMID,MONTH,YEAR
ORDER BY CREATED DESC,GYMID,MONTH,YEAR )AS LASTSET,
LEAD (CREATEDBY) OVER(PARTITION BY GOALTYPEID,GYMID,MONTH,YEAR
ORDER BY CREATED DESC,GYMID,MONTH,YEAR )AS LASTSETBY
FROM GOAL M
)
SELECT GOALID,A.GOALTYPEID,B.TYPE,GYMID,
MONTH,YEAR,GOAL,CREATED,CREATEDBY,
LASTGOALID,LASTVALUE,LASTSET,LASTSETBY
FROM CTE A JOIN GOALTYPE B
ON A.GOALTYPEID = B.GOALTYPEID
WHERE A.RN = 1

Column into rows and make a master table of columns

I need insert columns data to a different table and create master table for columns
Like example : I need to convert table tblcatData into two tables tblCat and tblcatDataNew
tblCatData
Primaykey | A | B | C | D | D | F | G | H | I | J | K | L | M |
--------------------------------------------------------------------
1 | 1 | 2 | 3 | 5 | 5 | 5 | 3 | 3 | 3 | 1 | 4 | 1 | 1 |
2 | 1 | 2 | 5 | 5 | 5 | 5 | 3 | 5 | 3 | 1 | 1 | 5 | 1 |
3 | 5 | 2 | 3 | 5 | 5 | 5 | 5 | 3 | 3 | 1 | 1 | 1 | 4 |
tblCat
PrimaryKey | Category
----------------------------
1 | A
2 | B
3 | C
4 | D
5 | E
6 | F
7 | G
. .
. .
. .
tblCatDataNew
PrimaryKey | FK_CatID | Data |
-----------------------------------
1 | 1 | 1 |
2 | 1 | 1 |
3 | 1 | 5 |
4 | 2 | 2 |
5 | 2 | 2 |
6 | 2 | 2 |
7 | 3 | 3 |
8 | 3 | 5 |
. . .
. . .
. . .
You could try the following scenario:
Create tblCat.
Create tblCatDataNew with the following deviations from the original design:
the FK_CatID column is allowed to accept NULLs temporarily (or maybe permanently, if that was your original intention);
an extra column is added temporarily to receive the category names from the original table.
Unpivot tblCatData and insert the results into tblCatDataNew (the values into Data and the column names, as category names, into the temporary column).
Select all the distinct category names from tblCatDataNew and insert them into tblCat. (That will produce the key values for them.)
Update the foreign keys in tblCatDataNew from tblCat, joining the two tables by category names.
Drop the temporary column from tblCatDataNew.
Set tblCatDataNew.FK_CatID as NOT NULL (that is, if you wanted it to be so).
Here's the entire test script, including creation of the original table (in case someone would like to try it):
BEGIN TRANSACTION
GO
/* prepare the original table, for tests */
WITH data (
Primaykey, A, B, C, D, E, F, G, H, I, J, K, L, M
) AS (
SELECT 1 , 1, 2, 3, 5, 5, 5, 3, 3, 3, 1, 4, 1, 1 UNION ALL
SELECT 2 , 1, 2, 5, 5, 5, 5, 3, 5, 3, 1, 1, 5, 1 UNION ALL
SELECT 3 , 5, 2, 3, 5, 5, 5, 5, 3, 3, 1, 1, 1, 4
)
SELECT * INTO tblCatData FROM data;
GO
/* Step 1 */
CREATE TABLE tblCat (
PrimaryKey int IDENTITY CONSTRAINT PK_tblCat PRIMARY KEY,
Category varchar(50) NOT NULL
);
GO
/* Step 2 */
CREATE TABLE tblCatDataNew (
PrimaryKey int IDENTITY CONSTRAINT PK_tblCatDataNew PRIMARY KEY,
FK_CatID int NULL CONSTRAINT FK_tblCatDataNew_tblCat FOREIGN KEY REFERENCES tblCat (PrimaryKey),
Data int,
Category varchar(50)
);
GO
/* Step 3 */
INSERT INTO tblCatDataNew (
Data,
Category
)
SELECT
Data,
Category
FROM tblCatData
UNPIVOT (
Data for Category IN (A, B, C, D, E, F, G, H, I, J, K, L, M)
) u
ORDER BY
Category,
Primaykey;
GO
/* Step 4 */
INSERT INTO tblCat (Category)
SELECT DISTINCT Category
FROM tblCatDataNew
GO
/* Step 5 */
UPDATE tblCatDataNew
SET FK_CatID = c.PrimaryKey
FROM tblCat c
WHERE tblCatDataNew.Category = c.Category
GO
/* Step 6 */
ALTER TABLE tblCatDataNew
DROP COLUMN Category
GO
/* Step 7 */
ALTER TABLE tblCatDataNew
ALTER COLUMN FK_CatID int NOT NULL
GO
/* view the results */
SELECT * FROM tblCat
SELECT * FROM tblCatDataNew
GO
ROLLBACK TRANSACTION
Note that the UNPIVOT clause is supported in SQL Server starting from the 2005 version. In earlier versions you'd have to use a different method to unpivot data (Step 3), e.g. like this:
INSERT INTO tblCatDataNew (
Data,
Category
)
SELECT
Data = CASE x.CatNum
WHEN 1 THEN A
WHEN 2 THEN B
WHEN 3 THEN C
WHEN 4 THEN D
WHEN 5 THEN E
WHEN 6 THEN F
WHEN 7 THEN G
WHEN 8 THEN H
WHEN 9 THEN I
WHEN 10 THEN J
WHEN 11 THEN K
WHEN 12 THEN L
WHEN 13 THEN M
END,
Category = CASE x.CatNum
WHEN 1 THEN 'A'
WHEN 2 THEN 'B'
WHEN 3 THEN 'C'
WHEN 4 THEN 'D'
WHEN 5 THEN 'E'
WHEN 6 THEN 'F'
WHEN 7 THEN 'G'
WHEN 8 THEN 'H'
WHEN 9 THEN 'I'
WHEN 10 THEN 'J'
WHEN 11 THEN 'K'
WHEN 12 THEN 'L'
WHEN 13 THEN 'M'
END
FROM tblCatData
CROSS JOIN (
SELECT 1 UNION ALL
SELECT 2 UNION ALL
SELECT 3 UNION ALL
SELECT 4 UNION ALL
SELECT 5 UNION ALL
SELECT 6 UNION ALL
SELECT 7 UNION ALL
SELECT 8 UNION ALL
SELECT 9 UNION ALL
SELECT 10 UNION ALL
SELECT 11 UNION ALL
SELECT 12 UNION ALL
SELECT 13
) x (CatNum)
ORDER BY
Category,
Primaykey;
or even like this:
INSERT INTO tblCatDataNew (
Data,
Category
)
SELECT
Data = CASE x.CatNum
WHEN 1 THEN A
WHEN 2 THEN B
WHEN 3 THEN C
WHEN 4 THEN D
WHEN 5 THEN E
WHEN 6 THEN F
WHEN 7 THEN G
WHEN 8 THEN H
WHEN 9 THEN I
WHEN 10 THEN J
WHEN 11 THEN K
WHEN 12 THEN L
WHEN 13 THEN M
END,
Category = x.CatName
FROM tblCatData
CROSS JOIN (
SELECT 1, 'A' UNION ALL
SELECT 2, 'B' UNION ALL
SELECT 3, 'C' UNION ALL
SELECT 4, 'D' UNION ALL
SELECT 5, 'E' UNION ALL
SELECT 6, 'F' UNION ALL
SELECT 7, 'G' UNION ALL
SELECT 8, 'H' UNION ALL
SELECT 9, 'I' UNION ALL
SELECT 10, 'J' UNION ALL
SELECT 11, 'K' UNION ALL
SELECT 12, 'L' UNION ALL
SELECT 13, 'M'
) x (CatNum, CatName)
ORDER BY
Category,
Primaykey;
Here are the results that the above script produced for me:
tblCat:
PrimaryKey Category
----------- --------------------------------------------------
1 A
2 B
3 C
4 D
5 E
6 F
7 G
8 H
9 I
10 J
11 K
12 L
13 M
tblCatDataNew:
PrimaryKey FK_CatID Data
----------- ----------- -----------
1 1 1
2 1 1
3 1 5
4 2 2
5 2 2
6 2 2
7 3 3
8 3 5
9 3 3
10 4 5
11 4 5
12 4 5
13 5 5
14 5 5
15 5 5
16 6 5
17 6 5
18 6 5
19 7 3
20 7 3
21 7 5
22 8 3
23 8 5
24 8 3
25 9 3
26 9 3
27 9 3
28 10 1
29 10 1
30 10 1
31 11 4
32 11 1
33 11 1
34 12 1
35 12 5
36 12 1
37 13 1
38 13 1
39 13 4
You can use union to unpivot the columns, and select ... into to store the result in a new table:
select PrimaryKey
, FK_CatId
, Category
into tblCatDataNew
from (
select PrimaryKey
, 1
, A
from tblCatData
union all
select PrimaryKey
, 2
, B
from tblCatData
union all
...
)

Resources