Need to find which invoice has the second-lowest total price among invoices that do not include a sale of a FiredAlways stove.
I can manage to get the lowest, but not the second lowest.
What I have:
SELECT TOP 1 WITH TIES
I.InvoiceNbr,
I.InvoiceDt,
I.TotalPrice
FROM INVOICE I
WHERE EXISTS(
SELECT TOP 2 WITH TIES
I.InvoiceNbr
FROM INVOICE I
WHERE EXISTS (
SELECT FK_InvoiceNbr
FROM INV_LINE_ITEM
WHERE FK_StoveNbr NOT IN
(SELECT S.SerialNumber
FROM STOVE AS S
WHERE S.Type = 'FiredAlways'))
ORDER BY I.TotalPrice DESC)
GROUP BY I.InvoiceNbr, I.InvoiceDt, I.TotalPrice
ORDER BY I.TotalPrice ASC;
Data:
[INVOICE](
[InvoiceNbr] [numeric](18, 0) NULL,
[InvoiceDt] [datetime] NULL,
[TotalPrice] [numeric](18, 2) NULL,
[FK_CustomerID] [numeric](18, 0) NULL,
[FK_EmpID] [numeric](18, 0) NULL
[INV_LINE_ITEM](
[LineNbr] [numeric](18, 0) NULL,
[Quantity] [numeric](18, 0) NULL,
[FK_InvoiceNbr] [numeric](18, 0) NULL,
[FK_PartNbr] [numeric](18, 0) NULL,
[FK_StoveNbr] [numeric](18, 0) NULL,
[ExtendedPrice] [numeric](18, 2) NULL
[STOVE](
[SerialNumber] [int] NOT NULL,
[Type] [char](15) NOT NULL,
[Version] [char](15) NULL,
[DateOfManufacture] [smalldatetime] NULL,
[Color] [varchar](12) NULL,
[FK_EmpId] [int] NULL,
Wanted Output:
Invoice # date Price
--------- ------------ -------
206 02/03/2002 28.11
Two general approaches to get the nth lowest:
DECLARE #tbl TABLE(SomeInt INT);
INSERT INTO #tbl VALUES(10),(2),(35),(44),(52),(56),(27);
--Use a TOP n on the inner select and a TOP 1 with a reverse order on the outer:
SELECT TOP 1 innertbl.SomeInt
FROM
(
SELECT TOP 2 SomeInt
FROM #tbl
GROUP BY SomeInt
ORDER BY SomeInt
) AS innertbl
ORDER BY innertbl.SomeInt DESC;
--use a CTE with DENSE_RANK() (thx to dnoeth for the hint)
WITH AddSortNumber AS
(
SELECT SomeInt
,DENSE_RANK() OVER(ORDER BY SomeInt) AS SortNumber
FROM #tbl
)
SELECT SomeInt
FROM AddSortNumber
WHERE SortNumber=2
Try This:
WITH cte AS
(
SELECT
I.InvoiceNbr,
I.InvoiceDt,
I.TotalPrice,
dense_rank() over( ORDER BY I.TotalPrice ASC) as dnrnk
FROM INVOICE I
INNER JOIN
Inv_LineITEM
ON InvoiceNbr = FK_InvoiceNbr
INNER JOIN
Stove
ON
FK_StoveNbr = SerialNumber
WHERE Type != 'FiredAlways'
)
select InvoiceNbr,
InvoiceDt,
TotalPrice from cte where dnrnk=2
SELECT TOP 1 TotalPrice FROM (
SELECT TOP 2 TotalPrice FROM invoice
ORDER BY TotalPrice DESC
) AS em ORDER BY TotalPrice ASC
Essentially:
Find the top 2 TotalPrice in descending order.
Of those 2, find the top TotalPrice in ascending order.
The selected value is the second-highest TotalPrice.
If the TotalPrice isn't distinct, you can use SELECT DISTINCT TOP ... instead.
So I'm trying to get some data in tsql (MSSQL2014) where I use a subquery to get a sum of some foreign key'd data table.
The structure looks like this:
TABLE [AggregateData](
[Id] [bigint] IDENTITY(1,1) NOT NULL,
[Aggregate_UUID] [uniqueidentifier] NOT NULL,
[DataDate] [date] NOT NULL,
[SizeAvailable] [bigint] NOT NULL,
[SizeTotal] [bigint] NOT NULL,
[SizeUsed] [bigint] NOT NULL,
[PercentageUsed] [int] NOT NULL
)
TABLE [Aggregate](
[Id] [bigint] IDENTITY(1,1) NOT NULL,
[UUID] [uniqueidentifier] NOT NULL,
[Name] [nvarchar](255) NOT NULL,
[Cluster_UUID] [uniqueidentifier] NOT NULL,
[DiskTypeID] [int] NOT NULL
)
TABLE [DiskType](
[Id] [int] IDENTITY(1,1) NOT NULL,
[TypeName] [nvarchar](255) NULL
)
TABLE [Volume](
[Id] [bigint] IDENTITY(1,1) NOT NULL,
[UUID] [uniqueidentifier] NOT NULL,
[Name] [nvarchar](255) NOT NULL,
[Aggregate_UUID] [uniqueidentifier] NOT NULL,
[ServiceClassID] [int] NULL,
[ProtocolID] [int] NOT NULL,
[EnvironmentID] [int] NOT NULL
)
TABLE [VolumeData](
[Id] [bigint] IDENTITY(1,1) NOT NULL,
[Volume_UUID] [uniqueidentifier] NOT NULL,
[DataDate] [date] NOT NULL,
[SizeAvailable] [bigint] NOT NULL,
[SizeTotal] [bigint] NOT NULL,
[SizeUsed] [bigint] NOT NULL,
[PercentageUsed] [int] NOT NULL
)
Now in the end I need to get the following data:
DataDate, DiskType, AggregateSizes (Avail, Used, Total), Aggregated Volume Sizes (Sum of Avail, Used, Total of Volumes in that Aggregate)
I was thinking of using subqueries but when trying to get the values for a specific Aggregate only (for testing, easier for my to check) I get wrong values in the subquery.
Here is what I tried;
SELECT
AggregateData.DataDate,
AggregateData.SizeTotal AS AggregateSizeTotal,
(SELECT
SUM(VolumeData.SizeTotal)
FROM VolumeData
LEFT JOIN Volume
ON VolumeData.Volume_UUID = Volume.UUID
WHERE Aggregate_UUID = Volume.Aggregate_UUID
AND VolumeData.DataDate = AggregateData.DataDate)
VolumeSizeTotal
FROM AggregateData
WHERE AggregateData.Aggregate_UUID = 'C58D0098-D1A4-4EE9-A0E9-7DE3EEB6275C'
ORDER BY AggregateData.DataDate
But this seems me to not get the correct value for the subquery sum. My subquery sum is way to high so I assume my where clause is incorrect (or the whole setup ;) ...)
So, question 1. Are subqueries the way to go or should I do it differently?
If (question 1 == true) Whats wrong with my subquery?
You need to qualify all column names. I would recommend using table abbreviations. The problem is Aggregate_UUID = v.Aggregate_UUID. The first column is coming from v so this is (essentially) a no-op.
Presumably, you want this correlated with the outer query:
SELECT ad.DataDate, ad.SizeTotal AS AggregateSizeTotal,
(SELECT SUM(vd.SizeTotal)
FROM VolumeData vd LEFT JOIN
Volume v
ON vd.Volume_UUID = v.UUID
WHERE ad.Aggregate_UUID = v.Aggregate_UUID AND
ad.DataDate = vd.DataDate
) VolumeSizeTotal
FROM AggregateData ad
WHERE ad.Aggregate_UUID = 'C58D0098-D1A4-4EE9-A0E9-7DE3EEB6275C'
ORDER BY ad.DataDate
You can do this using JOIN instead of correlated subquery (O(n^2) performance) -
SELECT
t1.DataDate,
t1.SizeTotal AS AggregateSizeTotal,
t2.total VolumeSizeTotal
FROM AggregateData t1 left join (SELECT
DataDate, SUM(VolumeData.SizeTotal) total
FROM VolumeData
LEFT JOIN Volume
ON VolumeData.Volume_UUID = Volume.UUID
WHERE Aggregate_UUID = Volume.Aggregate_UUID
group by DataDate) t2 on t1.datadate = t2.dataDate
WHERE t1.Aggregate_UUID = 'C58D0098-D1A4-4EE9-A0E9-7DE3EEB6275C';
Going for a query to return the desired end results, I would use something like this:
Now in the end I need to get the following data: DataDate, DiskType, AggregateSizes (Avail, Used, Total), Aggregated Volume Sizes (Sum of Avail, Used, Total of Volumes in that Aggregate)
select
AggregateUuid = a.uuid
, DiskType = dt.TypeName
, DataDate = ad.DataDate
, AggregateSizeAvailable = ad.SizeAvailable
, AggregateSizeUsed = ad.SizeUsed
, AggregateSizeTotal = ad.SizeTotal
, VolumeSizeAvailable = sum(vd.SizeAvailable)
, VolumeSizeUsed = sum(vd.SizeUsed)
, VolumeSizeTotal = sum(vd.SizeTotal)
from [Aggregate] a
inner join DiskType dt on dt.Id = a.DiskTypeId
inner join AggregateData ad on ad.Aggregate_uuid = a.uuid
left join Volume v on v.Aggregate_uuid = a.uuid
left join VolumeData vd on vd.Volume_uuid = v.uuid
and vd.DataDate = ad.DataDate
where a.uuid = 'C58D0098-D1A4-4ee9-A0E9-7de3eeb6275C'
group by
a.uuid
, dt.TypeName
, ad.DataDate
, ad.SizeAvailable
, ad.SizeUsed
, ad.SizeTotal
order by a.uuid, ad.DataDate;
test setup: http://rextester.com/HZZHLI45077
create table DiskType(
Id int identity(1,1) not null
, TypeName nvarchar(255) null
);
set identity_insert DiskType on;
insert into DiskType (Id, TypeName) values
(1,'Type1'), (2,'Type2');
set identity_insert DiskType off;
create table [Aggregate](
Id bigint identity(1,1) not null
, uuid uniqueidentifier not null
, Name nvarchar(255) not null
, Cluster_uuid uniqueidentifier not null
, DiskTypeid int not null
);
insert into [Aggregate] (uuid, name, cluster_uuid, disktypeid)
select 'C58D0098-D1A4-4EE9-A0E9-7DE3EEB6275C', 'ex', newid(), 1;
create table AggregateData(
Id bigint identity(1,1) not null
, Aggregate_uuid uniqueidentifier not null
, DataDate date not null
, SizeAvailable bigint not null
, SizeTotal bigint not null
, SizeUsed bigint not null
, PercentageUsed int not null
);
insert into AggregateData
select 'C58D0098-D1A4-4EE9-A0E9-7DE3EEB6275C', '20170101', 12,100,87,87
union all select 'C58D0098-D1A4-4EE9-A0E9-7DE3EEB6275C', '20170102', 9,100,90,90
union all select 'C58D0098-D1A4-4EE9-A0E9-7DE3EEB6275C', '20170103', 6,100,93,93;
create table Volume(
Id bigint identity(1,1) not null
, uuid uniqueidentifier not null
, Name nvarchar(255) not null
, Aggregate_uuid uniqueidentifier not null
, ServiceClassid int null
, Protocolid int not null
, Environmentid int not null
);
insert into Volume
select '00000000-0000-0000-0000-000000000001', 'v1'
, 'C58D0098-D1A4-4EE9-A0E9-7DE3EEB6275C', null, 1, 1
union all select '00000000-0000-0000-0000-000000000002', 'v2'
, 'C58D0098-D1A4-4EE9-A0E9-7DE3EEB6275C', null, 1, 1
union all select '00000000-0000-0000-0000-000000000003', 'v3'
, 'C58D0098-D1A4-4EE9-A0E9-7DE3EEB6275C', null, 1, 1;
create table VolumeData(
Id bigint identity(1,1) not null
, Volume_uuid uniqueidentifier not null
, DataDate date not null
, SizeAvailable bigint not null
, SizeTotal bigint not null
, SizeUsed bigint not null
, PercentageUsed int not null
);
insert into VolumeData
select '00000000-0000-0000-0000-000000000001', '20170101', 4,33,29,88
union all select '00000000-0000-0000-0000-000000000002', '20170101', 4,33,29,88
union all select '00000000-0000-0000-0000-000000000003', '20170101', 4,34,29,87
union all select '00000000-0000-0000-0000-000000000001', '20170102', 3,33,30,91
union all select '00000000-0000-0000-0000-000000000002', '20170102', 3,33,30,91
union all select '00000000-0000-0000-0000-000000000003', '20170102', 3,34,30,90
union all select '00000000-0000-0000-0000-000000000001', '20170103', 2,33,31,94
union all select '00000000-0000-0000-0000-000000000002', '20170103', 2,33,31,94
union all select '00000000-0000-0000-0000-000000000003', '20170103', 2,34,31,93
go
/* -------------------------------------------------------- */
select
AggregateUuid = a.uuid
, DiskType = dt.TypeName
, DataDate = convert(varchar(10),ad.DataDate,121)
, AggregateSizeAvailable = ad.SizeAvailable
, AggregateSizeUsed = ad.SizeUsed
, AggregateSizeTotal = ad.SizeTotal
, VolumeSizeAvailable = sum(vd.SizeAvailable)
, VolumeSizeUsed = sum(vd.SizeUsed)
, VolumeSizeTotal = sum(vd.SizeTotal)
from [Aggregate] a
inner join DiskType dt on dt.Id = a.DiskTypeId
inner join AggregateData ad on ad.Aggregate_uuid = a.uuid
left join Volume v on v.Aggregate_uuid = a.uuid
left join VolumeData vd on vd.Volume_uuid = v.uuid
and vd.DataDate = ad.DataDate
where a.uuid = 'C58D0098-D1A4-4ee9-A0E9-7de3eeb6275C'
group by
a.uuid
, dt.TypeName
, ad.DataDate
, ad.SizeAvailable
, ad.SizeUsed
, ad.SizeTotal
order by a.uuid, ad.DataDate;
results in:
+--------------------------------------+----------+------------+------------------------+-------------------+--------------------+---------------------+----------------+-----------------+
| AggregateUuid | DiskType | DataDate | AggregateSizeAvailable | AggregateSizeUsed | AggregateSizeTotal | VolumeSizeAvailable | VolumeSizeUsed | VolumeSizeTotal |
+--------------------------------------+----------+------------+------------------------+-------------------+--------------------+---------------------+----------------+-----------------+
| c58d0098-d1a4-4ee9-a0e9-7de3eeb6275c | Type1 | 2017-01-01 | 12 | 87 | 100 | 12 | 87 | 100 |
| c58d0098-d1a4-4ee9-a0e9-7de3eeb6275c | Type1 | 2017-01-02 | 9 | 90 | 100 | 9 | 90 | 100 |
| c58d0098-d1a4-4ee9-a0e9-7de3eeb6275c | Type1 | 2017-01-03 | 6 | 93 | 100 | 6 | 93 | 100 |
+--------------------------------------+----------+------------+------------------------+-------------------+--------------------+---------------------+----------------+-----------------+
I have two tables:
1. TABLE [dbo].[ItemCategories](
[Id] [int] IDENTITY(1,1) NOT NULL,
[CategoryId] [int] NULL,
[StockId] [int] NULL,
2. TABLE [dbo].[Categories](
[Id] [int] IDENTITY(1,1) NOT NULL,
[ParentCategoryId] [int] NULL,
[CategoryName] [nvarchar](100) NULL,
[Slug] [nvarchar](150) NULL
And this query in SQL Server 2012
SELECT [CategoryName], [Slug], [ParentCategoryId], [Id]
FROM [Categories]
ORDER BY [ParentCategoryId] DESC
Which returns these rows
[CategoryName] [Slug] [ParentCategoryId] [Id]
Exercise exercise 42 46
Fashion fashion 42 47
And I have a second query:
SELECT COUNT(*)
FROM [ItemCategories]
WHERE CategoryId = '46' <--- This Id is the same as [Id] from the first query
How can I a modify the first query to add total count from the second query to the returned CategoryName column (as a single string) ?
Like this:
[CategoryName] [Slug] [ParentCategoryId] [Id]
Exercise (31) exercise 42 46
Fashion (56) fashion 42 47
I have created this join, but I don't know how to add the COUNT(*) as text
SELECT [CategoryName], [Slug], [ParentCategoryId], [Categories].[Id]
FROM [Categories]
INNER JOIN [ItemCategories] ON [Categories].[Id]=[ItemCategories].[CategoryId]
ORDER BY [ParentCategoryId] DESC
You can use the count(*) window function. I would put it in a separate column, but you can do:
SELECT [CategoryName] + ' (' + cast(count(*) over (partition by Id) as varchar(255)) + ')',
[Slug], [ParentCategoryId], [Id]
FROM [Categories]
ORDER BY [ParentCategoryId] DESC;
EDIT:
For two tables, use a JOIN and GROUP BY:
SELECT c.CategoryName + ' (' + cast(count(ic.Id) as varchar(255)) + ')',
c.Slug, c.ParentCategoryId, c.Id
FROM Categories c LEFT JOIN
ItemCategories ic
on ic.CategoryId = c.Id
GROUP BY c.CategoryName, c.slug, c.ParentCategoryId, c.id
ORDER BY ParentCategoryId DESC;
I am working with SQL sever table and at technical point I am stuck.
Below I am attaching a screen-shot of the table query and result also the required logic.
There are two portions of query, one is without any condition and second is with condition that select "qty" when parent_id is NULL else print 0,
I want to print the "qty" of sub-item when its qty differs from its parent's "qty"
Here is the script:
GO
CREATE TABLE [dbo].[#Temp_order](
[order_id] [int] IDENTITY(1,1) NOT NULL,
[orderdate] [datetime] NULL
) ON [PRIMARY]
GO
GO
CREATE TABLE [dbo].[#Temp_order_list](
[order_list_id] [int] IDENTITY(1,1) NOT NULL,
[order_id] [int] NOT NULL,
[qty] [int] NOT NULL,
[price] [money] NOT NULL,
[type] [int] NOT NULL,
[parent_id] [int] NULL,
CONSTRAINT [PK_Temp_order_list] PRIMARY KEY CLUSTERED
(
[order_list_id] 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
/*
*/
INSERT INTO #Temp_order
( orderdate )
VALUES ( '2015-01-16 05:08:53' -- orderdate - datetime
)
INSERT INTO #Temp_order_list
( order_id, qty, price, type, parent_id )
VALUES ( (SELECT MAX(order_id) FROM #Temp_order), -- order_id - int
1, -- qty - int
10, -- price - money
2, -- type - int
NULL -- parent_id - int
)
DECLARE #ParentID AS INTEGER
SELECT #ParentID = MAX(order_list_id) FROM #Temp_order_list
INSERT INTO #Temp_order_list
( order_id, qty, price, type, parent_id )
VALUES ( (SELECT MAX(order_id) FROM #Temp_order), -- order_id - int
1, -- qty - int
12, -- price - money
3, -- type - int
#ParentID -- parent_id - int
)
INSERT INTO #Temp_order_list
( order_id, qty, price, type, parent_id )
VALUES ( (SELECT MAX(order_id) FROM #Temp_order), -- order_id - int
4, -- qty - int
13, -- price - money
3, -- type - int
#ParentID -- parent_id - int
)
SELECT * FROM #Temp_order_list WHERE order_id = 1
SELECT #Temp_order.order_id ,#Temp_order_list.order_list_id,#Temp_order_list.price,#Temp_order_list.type,
CASE WHEN #Temp_order_list.parent_id IS NOT NULL THEN 0
ELSE
ISNULL(#Temp_order_list.qty ,'') END AS quantity
FROM #Temp_order
INNER JOIN #Temp_order_list ON #Temp_order.order_id = #Temp_order_list.order_id
WHERE #Temp_order.order_id = 1
DROP TABLE #Temp_order
DROP TABLE #Temp_order_list
Any better solution?
Looking at your last comment, I can suggest to add self join and check that condition :
SELECT ordr.order_id,
list.order_list_id,
list.price,
list.type,
CASE
WHEN parentlist.order_list_id IS NOT NULL THEN list.qty
WHEN list.parent_id IS NOT NULL THEN 0
ELSE ISNULL(list.qty, '')
END AS quantity
FROM #Temp_order ordr
INNER JOIN #Temp_order_list list
ON ordr.order_id = list.order_id
LEFT JOIN #Temp_order_list parentlist
ON parentlist.order_list_id = list.parent_id
AND list.qty > parentlist.qty
AND list.type = 3
WHERE ordr.order_id = 1
I have created a dummy scenario that reflect many of the queries I have to write to check that some data we are importing is correct.
The example would be when you have 3 tables
Store
Customer
CustomerOrder
A Customer can belong to many stores but can only 1 OrderOnsale can be bought x customer x store.
Cannot seem to get it right. Below is tables and noddy data + my attempt.
IF object_id(N'Store', 'U') IS NOT NULL
DROP TABLE Store
GO
CREATE TABLE [dbo].[Store]
(
[Id] [bigint] NOT NULL,
[StoreName] [varchar](50) NOT NULL,
CONSTRAINT [PK_Store] PRIMARY KEY CLUSTERED ([Id] ASC)
) ON [PRIMARY]
GO
IF object_id(N'Customer', 'U') IS NOT NULL
DROP TABLE Customer
GO
CREATE TABLE [dbo].[Customer]
(
[CustomerId] [bigint] NOT NULL,
[StoreId] [bigint] NOT NULL,
[Name] [varchar](50) NOT NULL,
[Surname] [varchar](50) NOT NULL,
CONSTRAINT [PK_Customer] PRIMARY KEY CLUSTERED ([CustomerId] ASC)
) ON [PRIMARY]
GO
IF object_id(N'CustomerOrder', 'U') IS NOT NULL
DROP TABLE CustomerOrder
GO
CREATE TABLE [dbo].[CustomerOrder]
(
[OrderId] [bigint] NOT NULL,
[CustomerId] [bigint] NOT NULL,
[OrderName] [varchar](50) NOT NULL,
[OnSale] [bit] NOT NULL,
CONSTRAINT [PK_CustomerOrder] PRIMARY KEY CLUSTERED([OrderId] ASC)
) ON [PRIMARY]
GO
begin tran
INSERT INTO [dbo].[Store]([Id], [StoreName])
SELECT 1, N'Harrods' UNION ALL
SELECT 2, N'John Lewis'
INSERT INTO [dbo].[Customer]([CustomerId], [StoreId], [Name], [Surname])
SELECT 1, 1, N'John', N'Smith' UNION ALL
SELECT 2, 2, N'Joe', N'Blogg'
INSERT INTO [dbo].[CustomerOrder]([OrderId], [CustomerId], [OrderName], [OnSale])
SELECT 1, 1, N'Toys', 1 UNION ALL
SELECT 2, 1, N'Laptop', 1 UNION ALL
SELECT 3, 2, N'Toys', 0
commit
My incomplete attempt:
SELECT
HasCustomerBoughtMoreThanO1ItemOnSale =
CASE WHEN Count(T2.TotalBoughtOnSale) > 1 THEN 1 ELSE 0 END
FROM
CustomerOrder co1
INNER JOIN
customer c1 ON co1.CustomerId = c1.CustomerId
INNER JOIN
STORE S01 ON C1.StoreId = S01.Id
JOIN
(SELECT
CO2.CustomerId, S2.Id AS StoreId,
Count(CO2.OnSale) TotalBoughtOnSale
FROM
CustomerOrder CO2
INNER JOIN
customer c2 ON c2.CustomerId = CO2.CustomerId
INNER JOIN
STORE S2 ON C2.StoreId = S2.Id
WHERE
CO2.OnSale = 1
GROUP BY
CO2.CustomerId, S2.Id) AS t2 ON c1.CustomerId = T2.CustomerId
AND S01.Id = t2.StoreId
If what your after is if a single customer has bought more then one OnSale item then this query will do the trick.
SELECT
CO.CustomerId, C.StoreId
FROM CustomerOrder CO
INNER JOIN Customer C ON CO.CustomerId = C.CustomerId
WHERE OnSale = 1
GROUP BY CO.CustomerId, C.StoreId
HAVING COUNT(*) > 1
I should add that in this its given that a Customer can only shop in a single Store due to StoreId is a column in the Customer table.