I have this script that creates a table and store Sale Order information. The scenario behind it is that once and Item is added into Sale Order it's Status is 'A' means Add. Later somehow customer wants that item to be removed so we add a new row with same details but Status as 'D' means Delete.
Now I want to get only active Sale Order Items which should not include that item which was Added and then Removed from Order.
Here's my script.
CREATE TABLE [dbo].[SALE_DETAIL](
[ORDER_NUMBER] [varchar](50) NULL,
[ITEM_NAME] [varchar](250) NULL,
[QUANTITY] [int] NULL,
[PRICE] [numeric](18, 0) NULL,
[Status] [varchar](50) NULL
) ON [PRIMARY]
GO
SET ANSI_PADDING OFF
GO
INSERT [dbo].[SALE_DETAIL] ([ORDER_NUMBER], [ITEM_NAME], [QUANTITY], [PRICE], [Status]) VALUES (N'SO-100-ORD-19', N'Double Bed', 5, CAST(70000 AS Numeric(18, 0)), N'A')
GO
INSERT [dbo].[SALE_DETAIL] ([ORDER_NUMBER], [ITEM_NAME], [QUANTITY], [PRICE], [Status]) VALUES (N'SO-100-ORD-19', N'Sofa', 5, CAST(10000 AS Numeric(18, 0)), N'A')
GO
INSERT [dbo].[SALE_DETAIL] ([ORDER_NUMBER], [ITEM_NAME], [QUANTITY], [PRICE], [Status]) VALUES (N'SO-100-ORD-19', N'Dining Table', 1, CAST(50000 AS Numeric(18, 0)), N'A')
GO
INSERT [dbo].[SALE_DETAIL] ([ORDER_NUMBER], [ITEM_NAME], [QUANTITY], [PRICE], [Status]) VALUES (N'SO-100-ORD-19', N'Sofa', 5, CAST(10000 AS Numeric(18, 0)), N'D')
GO
The expected output I'm looking for should be something like this as Item 'Sofa' was cancelled from Order.
ORDER_NUMBER ITEM_NAME QTY PRICE
SO-100-ORD-19 Dining Table 1 50000
SO-100-ORD-19 Double Bed 5 70000
Query:
SELECT ORDER_NUMBER, ITEM_NAME, QUANTITY, PRICE FROM [dbo].[SALE_DETAIL]
WHERE Status <> 'D'
GROUP BY ORDER_NUMBER, ITEM_NAME, QUANTITY, PRICE
I would use NOT EXISTS:
SELECT SD.ORDER_NUMBER,
SD.ITEM_NAME,
SD.QUANTITY,
SD.PRICE
FROM dbo.[SALE_DETAIL] SD
WHERE NOT EXISTS (SELECT 1
FROM dbo.[SALE_DETAIL] e
WHERE e.ORDER_NUMBER = SD.ORDER_NUMBER
AND e.ITEM_NAME = SD.ITEM_NAME
AND e.[Status] = 'D');
Logically, the set-based answer is to use EXCEPT:
declare #SALE_DETAIL table([ORDER_NUMBER] [varchar](50) NULL,
[ITEM_NAME] [varchar](250) NULL,
[QUANTITY] [int] NULL,[PRICE] [numeric](18, 0) NULL,[Status] [varchar](50) NULL)
INSERT #SALE_DETAIL ([ORDER_NUMBER], [ITEM_NAME], [QUANTITY], [PRICE], [Status]) VALUES
(N'SO-100-ORD-19', N'Double Bed', 5, CAST(70000 AS Numeric(18, 0)), N'A'),
(N'SO-100-ORD-19', N'Sofa', 5, CAST(10000 AS Numeric(18, 0)), N'A'),
(N'SO-100-ORD-19', N'Dining Table', 1, CAST(50000 AS Numeric(18, 0)), N'A'),
(N'SO-100-ORD-19', N'Sofa', 5, CAST(10000 AS Numeric(18, 0)), N'D')
select Order_number,Item_name,Quantity,Price
from #SALE_DETAIL
where Status = 'A'
except
select Order_number,Item_name,Quantity,Price
from #SALE_DETAIL
where Status = 'D'
Which produces the results you've asked for. However, note that for whetever reason, this often seems to perform poorly in practice, in which case something like Larnu's Answer may be preferred.
In words: select all 'A'-records for which no matching 'D'-record exists.
In SQL:
SELECT ORDER_NUMBER, ITEM_NAME, QUANTITY, PRICE
FROM [dbo].[SALE_DETAIL] X
WHERE Status = 'A'
AND NOT EXISTS (
SELECT 1
FROM [dbo].[SALE_DETAIL] Y
WHERE Y.Status = 'D'
AND Y.ORDER_NUMBER = X.ORDER_NUMBER
AND Y.ITEM_NAME = X.ITEM_NAME
)
You can try get expected result using not in operator as shown below.
SELECT DISTINCT A.ORDER_NUMBER, A.ITEM_NAME,A.QUANTITY, A.PRICE, A.Status
FROM SALE_DETAIL A
where A.ITEM_NAME not in (select s.ITEM_NAME from SALE_DETAIL s
where s.[Status] = 'D')
The output is as shown below
ORDER_NUMBER ITEM_NAME QUANTITY PRICE Status
------------------------------------------------------
SO-100-ORD-19 Dining Table 1 50000 A
SO-100-ORD-19 Double Bed 5 70000 A
You can find the live demo Live Demo Here
Related
Below is the query where I am getting error
Declare #WorldpayAccountKeys varchar(max)='9132E282-2602-4D54-86CF-C93F9A56CF34
,E3759AFF-6968-40C1-8ABD-A25AE9176C1A
,B276D9C8-CC13-47C8-A469-76F8946AF6C0
,BCE689AD-CDC4-42D1-8718-69902ACCA9C3
,BCE689AD-CDC4-42D1-8718-69902ACCA9C4'
SELECT fooAccountId as abc, T.RowNum, T.Value
into #temp
FROM
(
SELECT O.Value, ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) as RowNum
FROM string_split(#WorldpayAccountKeys,',') O
) T
LEFT JOIN
foo R ON R.fooKey= T.Value
AND (R.foocId IN (11,12,13,14))
AND R.fooIndex = 0
I know error is for column fooAccountId which is primary key not null, I am getting one null record from this query for column fooAccountId
I have tried, applying null to primary key but it inserted intemp table , why not with above query
select fooAccountId as abc into #b from foo
union all
select null
Needed help, using sql server 2017
Sample query getting error:
CREATE TABLE [dbo].[WA](
[WAid] [bigint] IDENTITY(1,1) NOT NULL,
[WAKey] [uniqueidentifier] NOT NULL,
[WATypeRefKey] [char](1) NOT NULL,
[AccountId] [varchar](111) NULL,
[AccountToken] [varchar](111) NULL,
[AcceptorId] [varchar](111) NULL,
[ApplicationId] [varchar](111) NULL,
[ClientId] [int] NOT NULL,
[Version] [int] NOT NULL,
[VersionOn] [datetimeoffset](7) NOT NULL,
[VersionBy] [varchar](111) NOT NULL,
[RemovedIndex] [smallint] NOT NULL,
CONSTRAINT [WA_PKey] PRIMARY KEY CLUSTERED
(
[WAid] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
SET IDENTITY_INSERT [dbo].[WA] ON
GO
INSERT [dbo].[WA] ([WAid], [WAKey], [WATypeRefKey], [AccountId], [AccountToken], [AcceptorId], [ApplicationId], [ClientId], [Version], [VersionOn], [VersionBy], [RemovedIndex]) VALUES (1, N'9132e282-2602-4d54-86cf-c93f9a56cf34', N'Y', N'11', N'11', N'11', N'11', 11, 1, CAST(N'2022-11-30T00:00:00.0000000+00:00' AS DateTimeOffset), N'test', 0)
GO
INSERT [dbo].[WA] ([WAid], [WAKey], [WATypeRefKey], [AccountId], [AccountToken], [AcceptorId], [ApplicationId], [ClientId], [Version], [VersionOn], [VersionBy], [RemovedIndex]) VALUES (3, N'e3759aff-6968-40c1-8abd-a25ae9176c1a', N'Y', N'12', N'12', N'12', N'12', 12, 1, CAST(N'2022-12-01T18:14:11.5300000+00:00' AS DateTimeOffset), N'Test', 0)
GO
INSERT [dbo].[WA] ([WAid], [WAKey], [WATypeRefKey], [AccountId], [AccountToken], [AcceptorId], [ApplicationId], [ClientId], [Version], [VersionOn], [VersionBy], [RemovedIndex]) VALUES (6, N'b276d9c8-cc13-47c8-a469-76f8946af6c0', N'Y', N'13', N'13', N'13', N'13', 13, 1, CAST(N'2022-12-01T23:02:40.3164474+05:30' AS DateTimeOffset), N'Test', 0)
GO
INSERT [dbo].[WA] ([WAid], [WAKey], [WATypeRefKey], [AccountId], [AccountToken], [AcceptorId], [ApplicationId], [ClientId], [Version], [VersionOn], [VersionBy], [RemovedIndex]) VALUES (8, N'bce689ad-cdc4-42d1-8718-69902acca9c3', N'Y', N'134', N'14', N'14', N'14', 14, 1, CAST(N'2022-12-01T23:04:04.8758186+05:30' AS DateTimeOffset), N'Test', 0)
GO
INSERT [dbo].[WA] ([WAid], [WAKey], [WATypeRefKey], [AccountId], [AccountToken], [AcceptorId], [ApplicationId], [ClientId], [Version], [VersionOn], [VersionBy], [RemovedIndex]) VALUES (11, N'bce689ad-cdc4-42d1-8718-69902acca9c4', N'Y', N'135', N'15', N'15', N'15', 15, 1, CAST(N'2022-12-06T08:25:23.3533330+00:00' AS DateTimeOffset), N'Test', 0)
GO
Declare #WorldpayAccountKeys varchar(max)='9132E282-2602-4D54-86CF-C93F9A56CF34
,E3759AFF-6968-40C1-8ABD-A25AE9176C1A
,B276D9C8-CC13-47C8-A469-76F8946AF6C0
,BCE689AD-CDC4-42D1-8718-69902ACCA9C3
,BCE689AD-CDC4-42D1-8718-69902ACCA9C4'
drop table #temp
SELECT [WAid] as abc, T.RowNum, T.Value
into #temp
FROM
(
SELECT O.Value, ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) as RowNum
FROM string_split(#WorldpayAccountKeys,',') O
) T
LEFT JOIN
[WA] R ON R.[WAKey]= T.Value
AND (R.ClientId IN (11,12,13,14))
AND R.RemovedIndex = 0
The table that this SELECT INTO automatically creates is
CREATE TABLE #temp(
[abc] [bigint] IDENTITY(1,1) NOT NULL,
[RowNum] [bigint] NULL,
[Value] [varchar](max) NULL
)
The copying over of the IDENTITY property is probably causing the problem as this will require the column to be NOT NULL.
The reality is that the output of this column can be null in your query due to the outer join so not compatible with IDENTITY (which requires the NOT NULL).
You can suppress this copying over of the property by doing a no-op calculation to the column.
e.g.
SELECT [WAid]+0 as abc
instead of
SELECT [WAid] as abc
The resultant table definition is
CREATE TABLE #temp(
[abc] [bigint] NULL,
[RowNum] [bigint] NULL,
[Value] [varchar](max) NULL
)
Hi I have one doubt in sql server how to fetch date and filename information from filename field and with latest recrdods based _1 and _2 and _3. we need to consider _3 records count
Table
CREATE TABLE [dbo].[Filedetails](
[Filename] [varchar](50) NULL,
[id] [int] NULL,
[Recordscount] [int] NULL
)
INSERT [dbo].[Filedetails] ([Filename], [id], [Recordscount]) VALUES (N'leadgr_20220222.txt', 1, 10)
INSERT [dbo].[Filedetails] ([Filename], [id], [Recordscount]) VALUES (N'leadgr_20220222_1.txt', 2, 30)
INSERT [dbo].[Filedetails] ([Filename], [id], [Recordscount]) VALUES (N'payor_pc_20220222.txt', 3, 50)
INSERT [dbo].[Filedetails] ([Filename], [id], [Recordscount]) VALUES (N'ranun_uni_pc_20220222.txt', 4, 90)
INSERT [dbo].[Filedetails] ([Filename], [id], [Recordscount]) VALUES (N'ranun_uni_pc_20220222_1.txt', 5, 100)
INSERT [dbo].[Filedetails] ([Filename], [id], [Recordscount]) VALUES (N'ranun_uni_pc_20220222_2.txt', 6, 110)
INSERT [dbo].[Filedetails] ([Filename], [id], [Recordscount]) VALUES (N'sc_p2red20220309.txt', 7, 30)
based on above data I want output like below
FileName | Filedate |Recordscount
leadgr |2022-02-22 |30
payor_pc |2022-02-22 |50
ranun_uni_pc |2022-02-22 |110
sc_p2red |2022-03-29 |30
I have tried below
select
cast(left(right([Filename],12),8) as date)
,left( [Filename],len( [Filename])-13)
FROM [test].[dbo].[Filedetails]
above query not give expected result.could you please tell me how to write query to achive this task in sql server
I have customer Table ID and Customer Name
CREATE TABLE [dbo].[Customer]
(
[ID] [int] IDENTITY(1,1) NOT NULL,
[CustomerName] [nvarchar](500) NULL
) ON [PRIMARY]
I have Payment Schedule Table each customer may have different schedule
CREATE TABLE [dbo].[SchedulePayment]
(
[ID] [int] IDENTITY(1,1) NOT NULL,
[Date] [date] NULL,
[Amount] [decimal](18, 0) NULL,
[CustomerID] [int] NULL
) ON [PRIMARY]
I have ReceivedPayment table:
CREATE TABLE [dbo].[ReceivedPayment]
(
[ID] [int] IDENTITY(1,1) NOT NULL,
[CustomerID] [int] NULL,
[ReceivedAmount] [decimal](18, 0) NULL,
[ReceivedDate] [date] NULL
) ON [PRIMARY]
I want to deduct received amount from the schedule amount and show the remaining amount till today month and year future amount would be 0 because it is not due now but if customer pay extra then it will show in the future months
I want below output
SELECT rp.CustomerID,[date] ScheduleDate, ReceivedAmount,
(SumAmount -
SUM(ReceivedAmount)
OVER (PARTITION BY rp.CustomerID ORDER BY Receiveddate ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)
) AS PendingAmount
,Amount AS ScheduleAmount
FROM
(SELECT [date], Amount, CustomerID,
(SUM(amount)
OVER (PARTITION BY CustomerID ORDER BY [date] ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)
) AS SumAmount
FROM SchedulePayment) sp
JOIN ReceivedPayment rp ON rp.CustomerID = sp.CustomerID
AND ReceivedDate >= [date]
AND
(
(DATEPART(MM,ReceivedDate)=DATEPART(MM,[date]))
AND (DATEPART(YYYY,ReceivedDate)=DATEPART(YYYY,[date]))
)
ORDER BY rp.CustomerID,ReceivedDate,[date]
i looked at it too, and there is more going on here than can be explained at first glance. in normal payment posting, a balance carry forward rule to be held pending apply, and another rule would exist as the first of the month occurs, for payments in excess of payment due let alone no arrears. the solution has some rules than in all intense purposes is not necessarily database driven yet process and application driven from an accounting a/r perspective. here is some sample testing I did just to see how close I could come yet then realized expected output requires rules and dates and what I call float.
use test1
/*
CREATE TABLE [dbo].[Customer]
(
[ID] [int] IDENTITY(1,1) NOT NULL,
[CustomerName] [nvarchar](500) NULL
) ON [PRIMARY]
-- drop table customer
insert into customer select ('Customer1')
insert into customer select ('Customer2')
select * from customer
CREATE TABLE [dbo].[SchedulePayment]
(
[ID] [int] IDENTITY(1,1) NOT NULL,
[Date] [date] NULL,
[Amount] money NULL,
[CustomerID] [int] NULL
) ON [PRIMARY]
insert into SchedulePayment values ('2022-01-01', 1000, 1)
insert into SchedulePayment values ('2022-02-01', 1000, 1)
insert into SchedulePayment values ('2022-03-01', 1000, 1)
insert into SchedulePayment values ('2022-04-01', 1000, 1)
insert into SchedulePayment values ('2022-01-01', 1000, 2)
insert into SchedulePayment values ('2022-02-01', 1000, 2)
insert into SchedulePayment values ('2022-03-01', 1000, 2)
insert into SchedulePayment values ('2022-04-01', 1000, 2)
-- drop table schedulepayment
select * from schedulepayment
CREATE TABLE [dbo].[ReceivedPayment]
(
[ID] [int] IDENTITY(1,1) NOT NULL,
[CustomerID] [int] NULL,
[ReceivedAmount] money NULL,
[ReceivedDate] [date] NULL
) ON [PRIMARY]
insert into ReceivedPayment values (1, 500, '2022-01-06')
insert into ReceivedPayment values (1, 1500, '2022-02-15')
insert into ReceivedPayment values (1, 500, '2022-03-10')
insert into ReceivedPayment values (2, 100, '2022-01-01')
insert into ReceivedPayment values (2, 500, '2022-02-06')
insert into ReceivedPayment values (2, 400, '2022-02-08')
insert into ReceivedPayment values (2, 600, '2022-03-04')
-- drop table receivedpayment
select * from receivedpayment
*/
create table #t
(CustomerID int,
ScheduleDate date,
ReceivedAmount money,
PendingAmount money,
ScheduledAmount money
)
insert into #t (customerid, scheduledate, scheduledamount)
select
distinct customer.ID,
schedulepayment.date,
schedulepayment.Amount
from customer
left join schedulepayment on SchedulePayment.CustomerID = customer.ID
select * from #t
drop table #t
-- below is a pay flow by yet not a balance over paid and carry over to be spread
select
SchedulePayment.CustomerID,
SchedulePayment.Date,
ReceivedAmount = sum(ReceivedPayment.ReceivedAmount)
from ReceivedPayment
join SchedulePayment on ReceivedPayment.CustomerID = SchedulePayment.CustomerID
where datepart(month, SchedulePayment.Date) = datepart(month, ReceivedPayment.ReceivedDate) and datepart(year, schedulepayment.Date) = datepart(year, ReceivedPayment.ReceivedDate)
GROUP BY SchedulePayment.CustomerID, SchedulePayment.Date
here's the script to create the needed data:
CREATE TABLE [dbo].[TestXML](
[ID] [int] NOT NULL,
[PID] [int] NULL,
[Code] [int] NULL,
[Col1] [int] NULL,
[Col2] [int] NULL,
[Col3] [decimal](6, 2) NULL,
[Col4] [decimal](6, 2) NULL,
[Col5] [int] NULL,
CONSTRAINT [PK_TestXML] PRIMARY KEY CLUSTERED ([ID] ASC)
)
GO
CREATE TABLE [dbo].[XML_Columns_Reference](
[TableName] nvarchar(10) NULL,
[TableNameXML] nvarchar(10) NULL,
[ColumnName] nvarchar(10) NULL,
[ColumnNameXML] nvarchar(10) NULL,
[ColumnOrder] [int] NULL,
[GroupName] nvarchar(10) NULL,
[GroupOrder] [int] NULL,
[Category] [int] NULL
) ON [PRIMARY]
CREATE TABLE [dbo].[XML_NoUseCols_Ref](
[TableNameXML] nvarchar(10) NULL,
[Code] [int] NULL,
[ColumnName] nvarchar(10) NULL
) ON [PRIMARY]
GO
INSERT [dbo].[TestXML] ([ID], [PID], [Code], [Col1], [Col2], [Col3], [Col4], [Col5]) VALUES (1, 1000, 10, 1, 2, CAST(0.20 AS Decimal(6, 2)), CAST(0.10 AS Decimal(6, 2)), 1)
INSERT [dbo].[TestXML] ([ID], [PID], [Code], [Col1], [Col2], [Col3], [Col4], [Col5]) VALUES (2, 1000, 20, NULL, 1, CAST(1.00 AS Decimal(6, 2)), CAST(1.00 AS Decimal(6, 2)), 1)
INSERT [dbo].[TestXML] ([ID], [PID], [Code], [Col1], [Col2], [Col3], [Col4], [Col5]) VALUES (3, 1000, 30, NULL, NULL, CAST(2.00 AS Decimal(6, 2)), CAST(2.00 AS Decimal(6, 2)), 5)
INSERT [dbo].[XML_Columns_Reference] ([TableName], [TableNameXML], [ColumnName], [ColumnNameXML], [ColumnOrder], [GroupName], [GroupOrder], [Category]) VALUES (N'TestXML', N'T', N'Col1', N'N', 1, N'A', 2, NULL)
INSERT [dbo].[XML_Columns_Reference] ([TableName], [TableNameXML], [ColumnName], [ColumnNameXML], [ColumnOrder], [GroupName], [GroupOrder], [Category]) VALUES (N'TestXML', N'T', N'Col2', N'V', 2, N'A', 2, NULL)
INSERT [dbo].[XML_Columns_Reference] ([TableName], [TableNameXML], [ColumnName], [ColumnNameXML], [ColumnOrder], [GroupName], [GroupOrder], [Category]) VALUES (N'TestXML', N'T', N'Col3', N'N', 1, N'B', 3, NULL)
INSERT [dbo].[XML_Columns_Reference] ([TableName], [TableNameXML], [ColumnName], [ColumnNameXML], [ColumnOrder], [GroupName], [GroupOrder], [Category]) VALUES (N'TestXML', N'T', N'Col4', N'V', 2, N'B', 3, NULL)
INSERT [dbo].[XML_Columns_Reference] ([TableName], [TableNameXML], [ColumnName], [ColumnNameXML], [ColumnOrder], [GroupName], [GroupOrder], [Category]) VALUES (N'TestXML', N'T', N'Col5', N'A', 1, N'C', 1, NULL)
INSERT [dbo].[XML_NoUseCols_Ref] ([TableNameXML], [Code], [ColumnName]) VALUES (N'T', 20, N'Col1')
INSERT [dbo].[XML_NoUseCols_Ref] ([TableNameXML], [Code], [ColumnName]) VALUES (N'T', 30, N'Col1')
INSERT [dbo].[XML_NoUseCols_Ref] ([TableNameXML], [Code], [ColumnName]) VALUES (N'T', 30, N'Col2')
Here's the query I run:
DECLARE #PID int
SET #PID=1000
SELECT
GroupName as [#n],
(SELECT
Code [#n],
(SELECT
ColumnNameXML as [col/#n],
CASE name
WHEN 'Col1' THEN ISNULL(Col1,0)
WHEN 'Col2' THEN ISNULL(Col2,0)
WHEN 'Col3' THEN ISNULL(Col3,0)
WHEN 'Col4' THEN ISNULL(Col4,0)
WHEN 'Col5' THEN ISNULL(Col5,0)
END AS [col/col_value]
FROM sys.columns
CROSS JOIN TestXML ls1
INNER JOIN XML_Columns_Reference r1
ON sys.columns.name=r1.columnname
WHERE object_id = OBJECT_ID('TestXML') and r1.TableNameXML='T'
and ls1.PID=#PID and ls1.ID=ls2.ID and r1.GroupName=r2.GroupName
and r1.ColumnName NOT IN (SELECT [ColumnName]
FROM XML_NoUseCols_Ref
WHERE TableNameXML='T' and [Code]=ls1.Code
GROUP BY ColumnName)
ORDER BY column_id
FOR XML PATH(''),TYPE
)
FROM TestXML ls2
WHERE ls2.PID=#PID
FOR XML PATH('cat'),TYPE
)
FROM sys.columns
CROSS JOIN TestXML ls3
INNER JOIN XML_Columns_Reference r2
ON sys.columns.name=r2.columnname
WHERE object_id = OBJECT_ID('TestXML') and ls3.PID=#PID and r2.TableNameXML='T'
GROUP BY GroupName
ORDER BY MIN(groupOrder)
FOR XML PATH('grp'),TYPE
Result:
What I need is when there is a reference in XML_NoUseCols_Ref for all the rows of a group (you can get group in XML_Columns_Reference) to eliminated the cat row in the XML.
In the example that would be cat n="30" in grp n="A"
Any kind of help would be appreciated.
Thank you.
Instead of repeating the logic in many places I'd suggest to use a CTE (something like an ad-hoc VIEW) to pre-fetch your data. The rest is rather simple nesting with correlated sub-queries and much better to read:
Hope I got your logic correctly...
WITH cte AS
(
SELECT ls1.ID,ls1.PID,ls1.Code
,r1.ColumnNameXML,r1.GroupName,r1.GroupOrder
,CASE name
WHEN 'Col1' THEN ISNULL(Col1,0)
WHEN 'Col2' THEN ISNULL(Col2,0)
WHEN 'Col3' THEN ISNULL(Col3,0)
WHEN 'Col4' THEN ISNULL(Col4,0)
WHEN 'Col5' THEN ISNULL(Col5,0)
END AS ColumnValue
FROM sys.columns
CROSS JOIN TestXML ls1
INNER JOIN XML_Columns_Reference r1
ON sys.columns.name=r1.columnname
WHERE object_id = OBJECT_ID('TestXML') and r1.TableNameXML='T'
and ls1.PID=#PID
and r1.ColumnName NOT IN ( SELECT [ColumnName]
FROM XML_NoUseCols_Ref
WHERE TableNameXML='T' and [Code]=ls1.Code
GROUP BY ColumnName)
)
SELECT GroupName AS [#n]
,(
SELECT Code AS [#n]
,(
SELECT cte3.ColumnNameXML AS [#n]
,cte3.ColumnValue AS [col_value]
FROM cte cte3
WHERE cte3.GroupName=cte1.GroupName AND cte3.Code=cte2.Code
ORDER BY cte3.ColumnNameXML
FOR XML PATH('col'),TYPE
)
FROM cte cte2
WHERE cte1.GroupName=cte2.GroupName
GROUP BY cte2.Code
ORDER BY cte2.Code
FOR XML PATH('cat'),TYPE
)
FROM cte cte1
GROUP BY GroupName,GroupOrder
ORDER BY GroupOrder
FOR XML PATH('grp');
I have the following tables:
CREATE TABLE [dbo].[Amounts](
[EmpID] [int] NULL,
[Amount] [smallmoney] NULL
)
GO
CREATE TABLE [dbo].[Employees](
[LastName] [varchar](30) NULL,
[FirstName] [varchar](20) NULL,
[LocationID] [int] NULL,
[EmpID] [int] IDENTITY(1,1) NOT NULL
)
GO
CREATE TABLE [dbo].[Locations](
[LocationID] [int] NOT NULL,
[City] [varchar](20) NULL
) ON [PRIMARY]
GO
SET ANSI_PADDING OFF
GO
INSERT [dbo].[Amounts] ([EmpID], [Amount]) VALUES (7, 4750.0000)
GO
INSERT [dbo].[Amounts] ([EmpID], [Amount]) VALUES (2, 15750.0000)
GO
INSERT [dbo].[Amounts] ([EmpID], [Amount]) VALUES (7, 18100.0000)
GO
INSERT [dbo].[Amounts] ([EmpID], [Amount]) VALUES (4, 21000.0000)
GO
INSERT [dbo].[Amounts] ([EmpID], [Amount]) VALUES (3, 18100.0000)
GO
INSERT [dbo].[Amounts] ([EmpID], [Amount]) VALUES (10, 41000.0000)
GO
INSERT [dbo].[Amounts] ([EmpID], [Amount]) VALUES (7, 25000.0000)
GO
INSERT [dbo].[Amounts] ([EmpID], [Amount]) VALUES (11, 21500.0000)
GO
INSERT [dbo].[Amounts] ([EmpID], [Amount]) VALUES (5, 9900.0000)
GO
INSERT [dbo].[Amounts] ([EmpID], [Amount]) VALUES (7, 95900.0000)
GO
INSERT [dbo].[Amounts] ([EmpID], [Amount]) VALUES (9, 55000.0000)
GO
SET IDENTITY_INSERT [dbo].[Employees] ON
GO
INSERT [dbo].[Employees] ([LastName], [FirstName], [LocationID], [EmpID]) VALUES (N'Andrews', N'Alex', 1, 1)
GO
INSERT [dbo].[Employees] ([LastName], [FirstName], [LocationID], [EmpID]) VALUES (N'Brown', N'Barry', 1, 2)
GO
INSERT [dbo].[Employees] ([LastName], [FirstName], [LocationID], [EmpID]) VALUES (N'Jones', N'Lee', 2, 3)
GO
INSERT [dbo].[Employees] ([LastName], [FirstName], [LocationID], [EmpID]) VALUES (N'Kendal', N'David', 1, 4)
GO
INSERT [dbo].[Employees] ([LastName], [FirstName], [LocationID], [EmpID]) VALUES (N'Birch', N'Eric', 1, 5)
GO
INSERT [dbo].[Employees] ([LastName], [FirstName], [LocationID], [EmpID]) VALUES (N'Kircher', N'Lisa', 4, 6)
GO
INSERT [dbo].[Employees] ([LastName], [FirstName], [LocationID], [EmpID]) VALUES (N'Williams', N'David', 1, 7)
GO
INSERT [dbo].[Employees] ([LastName], [FirstName], [LocationID], [EmpID]) VALUES (N'Marshall', N'John', NULL, 8)
GO
INSERT [dbo].[Employees] ([LastName], [FirstName], [LocationID], [EmpID]) VALUES (N'Howard', N'James', 2, 9)
GO
INSERT [dbo].[Employees] ([LastName], [FirstName], [LocationID], [EmpID]) VALUES (N'O''Donnell', N'Terry', 2, 10)
GO
INSERT [dbo].[Employees] ([LastName], [FirstName], [LocationID], [EmpID]) VALUES (N'Smythe', N'Sally', 1, 11)
GO
INSERT [dbo].[Employees] ([LastName], [FirstName], [LocationID], [EmpID]) VALUES (N'Donovan', N'Barbara', 4, 12)
GO
INSERT [dbo].[Employees] ([LastName], [FirstName], [LocationID], [EmpID]) VALUES (N'Wagner', N'Phil', 1, 13)
GO
SET IDENTITY_INSERT [dbo].[Employees] OFF
GO
INSERT [dbo].[Locations] ([LocationID], [City]) VALUES (1, N'Paris')
GO
INSERT [dbo].[Locations] ([LocationID], [City]) VALUES (2, N'Sydney')
GO
INSERT [dbo].[Locations] ([LocationID], [City]) VALUES (3, N'Chicago')
GO
INSERT [dbo].[Locations] ([LocationID], [City]) VALUES (4, N'London')
GO
I want to generate XML which is ordered by the city which contains the highest overall amount first and then, nested within that, each employee (ordered by those with the highest total amount first) and then nested within each employee their amounts (highest first).
The following query (rather complex - please let me know if there is a simpler/cheaper way) gives the right ordering but I am stuck on how to get the XML(Elements) nesting as I have described.
;
WITH cte as
(
SELECT City,
FirstName,
LastName,
Amount,
SUM(Amount)OVER(PARTITION BY CONCAT(LastName, FirstName) ORDER BY CONCAT(LastName, FirstName) ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) AS Total
FROM Locations l
INNER JOIN Employees e
ON l.LocationID = e.LocationID
INNER JOIN Amounts a
ON a.EmpID = e.EmpID
GROUP BY City, LastName, FirstName, Amount
)
SELECT City, FirstName,LastName,Amount
FROM cte
ORDER BY SUM(Total)OVER(PARTITION BY City ORDER BY Total DESC ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) DESC, Total DESC, Amount DESC
Here's a simpler looking query giving the same results:
SELECT City,
FirstName,
LastName,
Amount
FROM Locations l
INNER JOIN Employees e
ON l.LocationID = e.LocationID
INNER JOIN Amounts a
ON a.EmpID = e.EmpID
GROUP BY City, LastName, FirstName, Amount
order by city,SUM(Amount)OVER(PARTITION BY CONCAT(LastName, FirstName) ORDER BY CONCAT(LastName, FirstName) ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) desc,Amount desc, firstname, lastname
SQLFiddle