I am trying to find out how many rows of a certain item exist in the table, e.g. in the following example for itemID 1 I need the result 5 (not 3, which is what I currently get). I am tempted to add TransactionID into the PARTITION BY clause, but that results in Msg 8120 since the query does not GROUP by TransactionID. Well, if it did then getting that count would be easy, but I do not want to group on Transaction Level. What can I do to get that ItemCount right? It must be so easy but I am banging my head.
DECLARE #t TABLE (TransactionID INT PRIMARY KEY IDENTITY, CustomerID INT, ItemID INT);
INSERT INTO #t (CustomerID, ItemID)
VALUES
(1, 1),
(2, 1),
(3, 1),
(4, 2),
(1, 1),
(2, 2),
(3, 3),
(4, 4),
(1, 1);
SELECT
CustomerID,
ItemID,
Rows = COUNT(*),
ItemRowCount = COUNT(*) OVER (PARTITION BY ItemID)
FROM
#t
GROUP BY
CustomerID,
ItemID
ORDER BY
ItemID,
CustomerID;
EDIT: I was overaggregating, I guess. Sebastian Meine got me on the track and his answer is right so I accepted it. However, this subquery works for my:
SELECT
CustomerID,
ItemID,
Rows = COUNT(*),
ItemRowCount = (SELECT COUNT(*) FROM #t x WHERE t.ItemID = x.ItemID)
FROM
#t t
GROUP BY
CustomerID,
ItemID
ORDER BY
ItemID,
CustomerID;
You need to pull your outer group count out of the actual group by query. The easiest way to do that is like this:
SQL Fiddle
MS SQL Server 2008 Schema Setup:
CREATE TABLE dbo.tbl (TransactionID INT PRIMARY KEY IDENTITY, CustomerID INT, ItemID INT);
INSERT INTO dbo.tbl (CustomerID, ItemID)
VALUES
(1, 1),
(2, 1),
(3, 1),
(4, 2),
(1, 1),
(2, 2),
(3, 3),
(4, 4),
(1, 1);
Query 1:
SELECT *,SUM(Rows)OVER(PARTITION BY ItemId) ItemCnt
FROM(
SELECT
CustomerID,
ItemID,
Rows = COUNT(*)
FROM
dbo.tbl
GROUP BY
CustomerID,
ItemID
)X
ORDER BY
ItemID,
CustomerID
Results:
| CUSTOMERID | ITEMID | ROWS | ITEMCNT |
----------------------------------------
| 1 | 1 | 3 | 5 |
| 2 | 1 | 1 | 5 |
| 3 | 1 | 1 | 5 |
| 2 | 2 | 1 | 2 |
| 4 | 2 | 1 | 2 |
| 3 | 3 | 1 | 1 |
| 4 | 4 | 1 | 1 |
Notice that I add the inner counts together instead of recounting from scratch.
You can use simple count and group by:
SELECT
ItemID,
ItemRowCount = COUNT(1)
FROM
#t
GROUP BY
ItemID
ORDER BY
ItemID
or if you need attach total rows count and item row count to every row:
SELECT
CustomerID,
ItemID,
Rows = COUNT(1) over (),
ItemRowCount = COUNT(1) OVER (PARTITION BY ItemID)
FROM
#t
ORDER BY
ItemID,
CustomerID;
To find out how many rows of a certain item exist in the table, we may not need CustomerId. Use following query -
DECLARE #t TABLE (TransactionID INT PRIMARY KEY IDENTITY, CustomerID INT, ItemID INT);
INSERT INTO #t (CustomerID, ItemID)
VALUES
(1, 1),
(2, 1),
(3, 1),
(4, 2),
(1, 1),
(2, 2),
(3, 3),
(4, 4),
(1, 1);
;WITH cte AS(
SELECT itemId, ROW_NUMBER() OVER (PARTITION BY itemId ORDER BY itemId DESC) AS row_cnt FROM #t
)
SELECT itemId, MAX(row_cnt) row_count FROM cte GROUP BY itemId
It will return -
itemId row_count
1 5
2 2
3 1
4 1
And if is case you need customerId, the use -
;WITH cte AS(
SELECT customerId, itemId, ROW_NUMBER() OVER (PARTITION BY itemId ORDER BY itemId DESC) AS row_cnt FROM #t
)
SELECT customerId, itemId, MAX(row_cnt) item_count FROM cte GROUP BY CustomerID, itemId
It will return -
customerId itemId item_count
1 1 5
2 1 2
3 1 3
2 2 1
4 2 2
3 3 1
4 4 1
You can use a CTE or join to a sub-table (like this)
SELECT
tbl.CustomerID,
tbl.ItemID,
Rows = COUNT(*),
ItemRowCount
FROM tbl
JOIN (SELECT ItemID, Count(*) as ItemRowCount
FROM tbl
GROUP BY ItemID) t ON tbl.ItemID = t.ItemID
GROUP BY
tbl.CustomerID,
tbl.ItemID,
ItemRowCount
ORDER BY
tbl.ItemID,
CustomerID;
or this
SELECT
tbl.CustomerID,
tbl.ItemID,
Rows = COUNT(*),
MAX(ItemRowCount)
FROM tbl
JOIN (SELECT ItemID, Count(*) as ItemRowCount
FROM tbl
GROUP BY ItemID) t ON tbl.ItemID = t.ItemID
GROUP BY
tbl.CustomerID,
tbl.ItemID
ORDER BY
tbl.ItemID,
CustomerID;
Related
My table structure as below
Category Sex Last Modified Date Id
7 2 2015-01-16 87603
7 1 2014-11-27 87729
7 2 2018-09-06 87135
7 1 2017-12-27 87568
My sql query as below
SELECT
MAX(Id) AS Id
FROM
Table
GROUP BY
Category, Sex
Result as below
87603
87729
But I would like to get Id as Max Last Modified Date. Correct result should be as below
87135
87568
You can use ROW_NUMBER() to find most recent row per group:
SELECT Id, LastModifiedDate
FROM (
SELECT Id, LastModifiedDate, ROW_NUMBER() OVER (PARTITION BY Category, Sex ORDER BY LastModifiedDate DESC) AS rnk
FROM t
) AS cte
WHERE rnk = 1
Use RANK() if you're interested in finding all rows with ties for LastModifiedDate.
You can also get it as
SELECT T.*
FROM
(
SELECT Sex,
MAX([Last Modified Date]) [Last Modified Date],
Category
FROM T
GROUP BY Sex,
Category
) TT INNER JOIN T ON T.[Last Modified Date] = TT.[Last Modified Date]
WHERE T.Sex = TT.Sex
AND
T.Category = TT.Category;
Returns:
+----------+-----+---------------------+-------+
| Category | Sex | Last Modified Date | Id |
+----------+-----+---------------------+-------+
| 7 | 2 | 06/09/2018 00:00:00 | 87135 |
| 7 | 1 | 27/12/2017 00:00:00 | 87568 |
+----------+-----+---------------------+-------+
We can get the solution by joining the same table with its grouped set:
SELECT MIN(T.Id)
FROM Table T
INNER JOIN (SELECT Category,
Sex,
MAX(LastModifiedDate) AS LastModifiedDate
FROM Table
GROUP BY Category, Sex) GT
ON GT.Category = T.Category
AND GT.Sex = T.Sex
AND GT.LastModifiedDate = T.LastModifiedDate
GROUP BY T.Category, T.Sex
Other option is to use correlated subquery :
select t.*
from table t
where t.LastModifiedDate = (select max(t1.LastModifiedDate)
from table t1
where t1.Category = t.Category and t1.Sex = t.Sex
);
Here are a few different approaches... (in no particular order)
IF OBJECT_ID('tempdb..#TestData', 'U') IS NOT NULL
DROP TABLE #TestData;
GO
CREATE TABLE #TestData (
Category TINYINT NOT NULL,
Sex TINYINT NOT NULL,
LastModifiedDate DATE NOT NULL,
Id INT NOT NULL
);
GO
INSERT #TestData(Category, Sex, LastModifiedDate, Id) VALUES
(7, 2, '2015-01-16', 87603),
(7, 1, '2014-11-27', 87729),
(7, 2, '2018-09-06', 87135),
(7, 1, '2017-12-27', 87568);
GO
/* nonclustered index to support the query. */
CREATE UNIQUE NONCLUSTERED INDEX ix_TestData_Category_Sex_LastModifiedDate
ON #TestData (Category ASC, Sex ASC, LastModifiedDate DESC)
INCLUDE (Id);
GO
--====================================================
-- option 1: TOP(n) WITH TIES...
SELECT TOP (1) WITH TIES
td.Id
FROM
#TestData td
ORDER BY
ROW_NUMBER() OVER (PARTITION BY td.Category, td.Sex ORDER BY td.LastModifiedDate DESC);
GO
-----------------------------------------------------
-- option 2: Filter on ROW_NUMBER()...
WITH
cte_AddRN AS (
SELECT
td.Id,
rn = ROW_NUMBER() OVER (PARTITION BY td.Category, td.Sex ORDER BY td.LastModifiedDate DESC)
FROM
#TestData td
)
SELECT
arn.Id
FROM
cte_AddRN arn
WHERE
arn.rn = 1;
GO
-----------------------------------------------------
-- option 3: binary concatination...
SELECT
Id = CONVERT(INT, SUBSTRING(MAX(bv.bin_val), 4, 4))
FROM
#TestData td
CROSS APPLY ( VALUES (CONVERT(BINARY(3), td.LastModifiedDate) + CONVERT(BINARY(4), td.Id)) ) bv (bin_val)
GROUP BY
td.Category,
td.Sex;
GO
--====================================================
I'm new to stackoverflow, but I'm stuck with my query.
I've got a SQL table whitch looks like this:
+-------+------------+
| col1 | col2 |
+-------+------------+
| 1 | 1 |
| 1 | 2 |
| 1 | 3 |
| 1 | 4 |
| 1 | 6 |
+-------+------------+
I don't know how to get the following resultset:
+-------+------------+
| col1 |SerialNumber|
+-------|------------+
| 1 | 1 to 4, 6 |
+--------------------+
With XML Path i can get this:
+-------+------------+
| col1 |SerialNumber|
+-------|------------+
| 1 | 1,2,3,4,6, |
+--------------------+
This is my query for it:
SELECT DISTINCT O.Col1,
(SELECT CAST(P.Col2 As varchar(5)) + ',' AS [text()]
FROM #Test P
WHERE P.Col1 = O.Col1
ORDER BY P.Col1
FOR XML PATH('')) AS 'SerialNumber'
FROM #Test O
I'm sorry if my question was already asked. I'm also lacking Keywords for this topic.
Test data:
CREATE TABLE t(col1 int,col2 int)
INSERT t(col1,col2)VALUES
(1,1),(1,2),(1,3),(1,4),
(1,6),(1,7),(1,8),(1,9),
(1,11),
(1,13),
(2,3),(2,4),(2,5),
(2,7)
A variant with FOR XML PATH:
SELECT col1,col2,outVal
INTO #temp
FROM
(
SELECT
col1,
col2,
outVal,
ISNULL(LEAD(outVal)OVER(PARTITION BY col1 ORDER BY col2),'') nextOutVal
FROM
(
SELECT
col1,
col2,
CASE
WHEN col2-1=LAG(col2)OVER(PARTITION BY col1 ORDER BY col2) AND col2+1=LEAD(col2)OVER(PARTITION BY col1 ORDER BY col2)
THEN 'to'
ELSE CAST(col2 AS varchar(10))
END outVal
FROM t
) q
) q
WHERE outVal<>nextOutVal
ORDER BY col1,col2
SELECT
t1.col1,
REPLACE(STUFF(
(
SELECT ','+t2.outVal
FROM #temp t2
WHERE t2.col1=t1.col1
ORDER BY t2.col2
FOR XML PATH('')
),1,1,''),',to,',' to ') SerialNumber
FROM (SELECT DISTINCT col1 FROM #temp) t1
DROP TABLE #temp
A variant for SQL Server 2017 (with STRING_AGG):
SELECT
col1,
REPLACE(STRING_AGG(outVal,',')WITHIN GROUP(ORDER BY col2),',to,',' to ')
FROM
(
SELECT
col1,
col2,
outVal,
ISNULL(LEAD(outVal)OVER(PARTITION BY col1 ORDER BY col2),'') nextOutVal
FROM
(
SELECT
col1,
col2,
CASE
WHEN col2-1=LAG(col2)OVER(PARTITION BY col1 ORDER BY col2) AND col2+1=LEAD(col2)OVER(PARTITION BY col1 ORDER BY col2)
THEN 'to'
ELSE CAST(col2 AS varchar(10))
END outVal
FROM t
) q
) q
WHERE outVal<>nextOutVal
GROUP BY col1
Result:
col1 SerialNumber
1 1 to 4,6 to 9,11,13
2 3 to 5,7
Solution:
Another possible approach using CTE for start and end values for each sequence and group concatenation:
T-SQL:
-- Table creation
CREATE TABLE #ValuesTable (
Col1 int,
Col2 int
)
INSERT INTO #ValuesTable VALUES (1, 1)
INSERT INTO #ValuesTable VALUES (1, 2)
INSERT INTO #ValuesTable VALUES (1, 3)
INSERT INTO #ValuesTable VALUES (1, 4)
INSERT INTO #ValuesTable VALUES (1, 6)
INSERT INTO #ValuesTable VALUES (2, 1)
INSERT INTO #ValuesTable VALUES (2, 2)
INSERT INTO #ValuesTable VALUES (2, 3)
INSERT INTO #ValuesTable VALUES (2, 4)
INSERT INTO #ValuesTable VALUES (2, 6)
INSERT INTO #ValuesTable VALUES (2, 7);
INSERT INTO #ValuesTable VALUES (2, 10);
-- Find sequences
WITH
TableStart AS (
SELECT t.Col1, t.Col2, ROW_NUMBER() OVER (ORDER BY t.Col1, t.Col2) AS RN
FROM #ValuesTable t
LEFT JOIN #ValuesTable b ON (t.Col1 = b.Col1) AND (t.Col2 = b.Col2 + 1)
WHERE (b.Col2 IS NULL)
),
TableEnd AS (
SELECT t.Col1, t.Col2, ROW_NUMBER() OVER (ORDER BY t.Col1, t.Col2) AS RN
FROM #ValuesTable t
LEFT JOIN #ValuesTable b ON (t.Col1 = b.Col1) AND (t.Col2 = b.Col2 - 1)
WHERE (b.Col2 IS NULL)
),
TableSequences AS (
SELECT
TableStart.Col1 AS Col1,
CASE
WHEN (TableStart.Col2 <> TableEnd.Col2) THEN CONVERT(nvarchar(max), TableStart.Col2) + N' to ' + CONVERT(nvarchar(max), TableEnd.Col2)
ELSE CONVERT(nvarchar(max), TableStart.Col2)
END AS Sequence
FROM TableStart
LEFT JOIN TableEnd ON (TableStart.RN = TableEnd.RN)
)
-- Select with group concatenation
SELECT
t1.Col1,
(
SELECT t2.Sequence + N', '
FROM TableSequences t2
WHERE t2.Col1 = t1.Col1
ORDER BY t2.Col1
FOR XML PATH('')
) SerialNumber
FROM (SELECT DISTINCT Col1 FROM TableSequences) t1
Output:
Col1 SerialNumber
1 1 to 4, 6,
2 1 to 4, 6 to 7, 10,
Notes:
Tested on SQL Server 2005, 2012, 2016.
Using T-SQL (SQL Server 2008 R2), I'm trying to list only the rows with the second highest value in a particular column from a temp table and then place the results into a new temp table. The PK is the ID, which can have increasing version numbers and then unique codes.
Example:
ID | Name| Version | Code
------------------------
1 | A | 1 | 10
1 | A | 2 | 20
1 | A | 3 | NULL
2 | B | 1 | 40
2 | B | 2 | 50
2 | C | 1 | 60
The desired outcome of the query is
ID | Version | Code
------------------------
1 | 2 | 20
2 | 1 | 40
To achieve this I need the below query to be adapted to pull the second highest value as long as the result gives a version number greater than 1. These results come from a temp table and will then be placed into a final results temp table. EDIT: Please note this will be applied over 33000 rows of data so I would prefer something neater than INSERT VALUES. Thanks.
Current query:
SELECT
ID
,Version
,Code
INTO
#table2
FROM
#table1
SELECT *
FROM #table2
WHERE Version > 1
ORDER BY ID asc
DROP TABLE #table1
DROP TABLE #table2
I have tried running the where clause WHERE Version < (SELECT MAX(VERSION) FROM #TABLE 2) but this has no effect, presumably due to the unique code values and in any case wouldn't work where I have more than 3 Versions.
Ideas would be gratefully received.
Thanks in advance.
i HAVE TEST THE BELOW CODE AND IT IS GIVING OUTPUT AS PER The YOUR desired outcome of the query is
SELECT ID,Name,[Version],Code
FROM (
SELECT ROW_NUMBER() OVER (PARTITION BY NAME ORDER BY [Version] DESC) AS RNK,*
FROM
(
SELECT 1 ID, 'A' Name ,1 [Version] ,10 Code
UNION ALL
SELECT 1, 'A', 2 ,20
UNION ALL
SELECT 1, 'A', 3 ,30
UNION ALL
SELECT 1, 'A', 4 ,NULL
UNION ALL
SELECT 2, 'B', 1 ,40
UNION ALL
SELECT 2, 'B', 2 ,50
UNION ALL
SELECT 2, 'C', 1 ,60
)B
)BASE
WHERE RNK =2
If your primary key is only ID, you have duplicate rows. So I assume your primary key is something else, for example ID, Version, Name. You have two rows with the same ID and same Version, what kind of rule do you want to apply on this ? Lowest number ?
I made an example that does kind of what you want:
First declare the necessary tables:
declare #table1 table (
Id int,
Name nvarchar(20),
[Version] int,
Code int
)
insert into #table1 values (1,'A',1,10),(1,'A',2,20),(1,'A',3,30),(1,'A',4,NULL)
,(2,'B',1,40),(2,'B',2,50),(2,'C',1,60);
And then the query to get the results:
with HighestVersions (Id, MaxVersion) As
(
select Id, max(version) from #table1 group by Id
)
select
t1.Id,
t1.[Version],
min(t1.Code) as Code
from
#table1 t1
inner join
HighestVersions hv
on
hv.Id = t1.Id
and (hv.MaxVersion-1) = t1.[Version]
group by
t1.Id
,t1.[Version]
I had to do a little dirty trick with the outermost select, this is because of the duplicate 'Id' and 'Version'. Else you would have gotten two rows with ID = 2, Version = 1
If you want to remove the NULL value you can change the WITH part (according to your last edit):
with HighestVersions (Id, MaxVersion) As
(
select Id, max(version) from #table1 where Code is not null group by Id
)
Try this:
DECLARE #List TABLE (ID int, Name char(1), Version int, Code int NULL)
INSERT INTO #List
VALUES
(1, 'A', 1, 10),
(1, 'A', 2, 20),
(1, 'A', 3, 30),
(1, 'A', 4, NULL),
(2, 'B', 1, 40),
(2, 'B', 2, 50),
(2, 'C', 1, 60)
SELECT
ID, Name, Version, Code
FROM
(
SELECT
*,
ROW_NUMBER() OVER (PARTITION BY ID, Name ORDER BY Version DESC) Rn
FROM #List
) a
WHERE
a.Rn = 2
I am working with SQL Server 2008. I have a table which does not contain any unique columns; how to get alternate rows from it?
SQL Server table:
+-----+--------+
| id | name |
|-----+--------|
| 1 | abc |
| 2 | pqr |
| 2 | pqr |
| 3 | xyz |
| 4 | lmn |
| 5 | efg |
| 5 | efg |
+-----+--------+
As we've to come with at least one working suggestion with the question, I've tried below code; which is not so proper technique when fetching from a huge amount of data.
Trial:
create table #tmp
(
id int, name varchar(10), srNo int
)
insert into #tmp
select
id, name,
ROW_NUMBER() OVER (ORDER BY id) % 2 as srNo --,alternate rows
from
Employee
select *
from #tmp
where srNo = 1 --or srNo = 0
Above query gives out alternate rows i.e. 1st, 3rd, 5th OR 2nd, 4th, 6th etc.
Please help me out with proper way without #tmp to achieve the goal!
You can just use your select statement as an in-line view. You don't need the #tmp table.
select t.id, name
from (select id, name, ROW_NUMBER() over (order by id) as srNo from Employee) t
where (t.srNo % 2) = 1
SqlFiddle
--To fetch ALTERNATE records from a table (EVEN NUMBERED)
Select * from TableName where ColumnName % 2 = 0
For Eg : select * from HumanResources.Employee where BusinessEntityID % 2 = 0
--To fetch ALTERNATE records from a table (ODD NUMBERED)
Select * from TableName where ColumnName % 2 = 1
For Eg : select * from HumanResources.Employee where BusinessEntityID % 2 = 1
I'm taking student as a table name.
Here is my answer ->
For Even Row Number -
> SELECT id from (SELECT rowno, id from student) where mod(rowno,2)=0
For Odd Row Number -
> SELECT id from (SELECT rowno, id from student) where mod(rowno,2)=1
Same also can be achieved using having clause; but it adds group by task:
SELECT id, name
FROM (SELECT id, name, ROW_NUMBER()over(order by id) AS srNo FROM Employee) x
GROUP BY srNo, id, name
HAVING (srNo % 2) = 0
You can just use your select statement as an in-line view. You don't need the #tblCities table.
select tbl1.CityID,tbl1.CityName from (select ROW_NUMBER() over(order by CityID asc) as row_no,CityID,CityName from tblCities) as tbl1 where tbl1.row_no%2=1
declare #t table
(
id int,
name nvarchar(20)
)
insert into #t
Select 1, 'abc'
union all
Select 2, 'pqr'
union all
Select 2, 'pqr'
union all
Select 3, 'xyz'
union all
Select 4, 'lmn'
union all
Select 5, 'efg'
union all
Select 2, 'efg'
Select * from(
Select *, row_number() over(order by id) as rnum from #t ) t where rnum % 2 <> 0
create table t (id bigint NOT NULL, input_1 boolean not null, data_gps timestamp(0) not null);
insert into t (id, input_1,data_gps) values
(1, false , '2022-01-01 15:42:07'),
(2, true , '2022-01-02 15:42:07'),
(3, true , '2022-01-03 15:42:07'),
(4, false , '2022-01-04 15:42:07'),
(5, true , '2022-01-05 15:42:07'),
(6, true , '2022-01-06 15:42:07'),
(7, true , '2022-01-07 15:42:07'),
(8, true , '2022-01-08 15:42:07'),
(9, false , '2022-01-09 15:42:07'),
(10 ,true , '2022-01-10 15:42:07'),
(11, true , '2022-01-11 15:42:07'),
(12, true , '2022-01-12 15:42:07'),
(13, false , '2022-01-13 15:42:07'),
(14, true , '2022-01-14 15:42:07');
you will have
Here is the query that will group by value change
select input_1, min(data_gps) as mind, max(data_gps) as maxd
from (
select input_1, data_gps,
row_number() over (order by data_gps)
- row_number() over (partition by input_1 order by data_gps) as grp
from t
) as tmp
group by input_1, grp
order by min(data_gps);
The results
DEMO
https://dbfiddle.uk/6Ajy3H5O
I have this table.
Bundles
id | parent_id | quantity
1 | 0 | 1
2 | 1 | 4
3 | 2 | 5
I want to get the total quantity of a bundle with id 3, which is 1 * 4 * 5 = 20 items
Can this be done with a single query?
Here's a solution using CTE:
Setup:
CREATE TABLE Table1
(id int, parent_id int, quantity int)
;
INSERT INTO Table1
(id, parent_id, quantity)
VALUES
(1, 0, 1),
(2, 1, 4),
(3, 2, 5),
(4, 0, 7),
(5, 4, 10)
;
CTE to return total of id=3 and it's parent items:
;WITH myCTE AS
(
SELECT id, parent_id, quantity
FROM Table1
WHERE id = 3
UNION ALL
SELECT T.id, T.parent_id, T.quantity
FROM Table1 T
JOIN myCTE C ON T.id = C.parent_id
)
SELECT EXP(sum(log(quantity)))
FROM myCTE
Demo SQL Fiddle
Multiplication method for values in a column, SELECT EXP(sum(log(quantity))), taken from here.