tSQL: Increase every group by 1 - tsql

Dear Forum and SQL specialists,
is it possible by SQL to increase every group change by 1?
I already tried the agg functions like Dense_Rank, Row_Number ... but without success.
Example:
Here you can find some test data
DECLARE #test TABLE ([Session] int, Sort int)
Insert into #test Values (1,1)
Insert into #test Values (1,2)
Insert into #test Values (1,3)
Insert into #test Values (0,4)
Insert into #test Values (0,5)
Insert into #test Values (1,6)
Insert into #test Values (1,7)
Insert into #test Values (1,8)
Insert into #test Values (1,9)
Insert into #test Values (1,10)
Insert into #test Values (1,11)
Insert into #test Values (1,12)
Insert into #test Values (1,13)
Insert into #test Values (0,14)
Insert into #test Values (1,15)
Insert into #test Values (0,16)
Insert into #test Values (1,17)
Insert into #test Values (1,18)
Insert into #test Values (1,19)
Insert into #test Values (1,20)
select * from #test

With LAG() and SUM() window functions:
SELECT SUM(flag) OVER (ORDER BY Sort) GroupNumber, Session, Sort
FROM (
SELECT *, CASE WHEN Session = LAG(Session) OVER (ORDER BY Sort) THEN 0 ELSE 1 END flag
FROM #test
) t
See the demo.

Related

Generate multiple rows for single column

I've data as below:
Create table #student(id int, name varchar(20))
create table #test(id int, test_Date datetime, test_type varchar(20))
Insert int #student values (1, 'A')
insert into #student values (2, 'B')
insert into #student values (3, 'C')
insert into #test values (1, '1/1/2022', 'Math')
insert into #test values (1, '1/2/2022', 'Eng')
insert into #test values (1, '1/3/2022', 'Science')
insert into #test values (2, '2/1/2022', 'Math')
insert into #test values (2, '2/2/2022', 'Eng')
insert into #test values (3, '3/1/2022', 'Math')
insert into #test values (3, '3/2/2022', 'Science')
Need data in the below format:
Output
Looks like you simply just want to join your #student table to your #test table.
SELECT s.id, s.name, t.test_date. t.test_type
FROM #student s
JOIN #test t
ON s.id = t.id
ORDER BY s.id, t.test_date, t.test_type
This will show the id, name, test date and test type per student.
Ordered by the student id, test date and test type.
try this
;with t0 AS (
SELECT s.id, s.name, t.test_date, t.test_type
,dense_rank() over(partition by s.id order by s.id ,test_date) AS drid
,dense_rank() over(partition by s.id,name order by s.id ,test_date) AS drname
FROM #student s
JOIN #test t
ON s.id = t.id
)
select case drid when 1 then id else null end as id
,case drname when 1 then name else null end as
name,test_Date,test_type from t0
ORDER BY t0.id, t0.name

T-SQL merging 2 rows into 1

I have a table where I end up with two rows but I need a total of the data from both rows in one row, I can't use group by as I'm getting the usual must be in select list or contained in the group by clause.
DECLARE #Test TABLE
(
FirstName NVARCHAR(10)
,Ref NVARCHAR (4)
,UserName NVARCHAR(30)
,File1 INT
,File2 INT
,ID INT
,Active Bit DEFAULT 0
)
INSERT INTO #Test VALUES ('John','AAAB','AAA Admin',5,10,677,1)
INSERT INTO #Test VALUES ('John (P)','AAAC','AAAC Admin',6,15,765,1)
INSERT INTO #Test VALUES ('John Admin','AAAG','AAA Admin',6,15,765,0)
INSERT INTO #Test VALUES ('Jane','AAUD','AAA Admin',6,15,765,0)
INSERT INTO #Test VALUES ('Jenny','AAOZ','AAA Admin',6,15,765,0)
;WITH CTE
AS
(
SELECT *,ROW_NUMBER() OVER(Partition by FirstName Order by ID) RN
from #Test
)
SELECT * FROM CTE
WHERE RN=1 AND Active = 1
ORDER BY ID DESC
[![Results][1]][1]
The File1 and File2 Values from INSERTS 3,4,5 are duplicate data and aren't required in the result.
Text :
FirstName Ref UserName File1 File2 ID Active RN
John (P) AAAC AAAC Admin 6 15 765 1 1
John AAAB AAA Admin 5 10 677 1 1
Expected Output:
I'm not sure what you want.
DECLARE #Test TABLE
(
FirstName NVARCHAR(10)
,Ref NVARCHAR (4)
,UserName NVARCHAR(30)
,File1 INT
,File2 INT
,ID INT
,Active Bit DEFAULT 0
)
INSERT INTO #Test VALUES ('John','AAAB','AAA Admin',5,10,677,1)
INSERT INTO #Test VALUES ('John (P)','AAAC','AAAC Admin',6,15,765,1)
INSERT INTO #Test VALUES ('John Admin','AAAG','AAA Admin',6,15,765,0)
INSERT INTO #Test VALUES ('Jane','AAUD','AAA Admin',6,15,765,0)
INSERT INTO #Test VALUES ('Jenny','AAOZ','AAA Admin',6,15,765,0)
;WITH CTE
AS
(
SELECT
IIF(Tmp = 0, A.FirstName, SUBSTRING(A.FirstName, 0, A.Tmp)) FirstName,
A.File1,
A.File2
FROM
(
SELECT
*,
CHARINDEX(' ', FirstName, 0) Tmp
FROM
#Test
WHERE
Active = 1
) A
)
SELECT
A.FirstName,
SUM(A.File1) File1,
SUM(A.File2) File2
FROM
CTE A
GROUP BY
A.FirstName
Result:
FirstName File1 File2
John 11 25

T-SQL Date week day date ranges

I have a simple query to return two columns:
DECLARE #Test TABLE
(
Product VARCHAR(100)
)
INSERT INTO #Test VALUES ('Hats')
INSERT INTO #Test VALUES ('Hats')
INSERT INTO #Test VALUES ('Hats')
INSERT INTO #Test VALUES ('Hats')
INSERT INTO #Test VALUES ('Shirts')
INSERT INTO #Test VALUES ('Shirts')
INSERT INTO #Test VALUES ('Trousers')
INSERT INTO #Test VALUES ('Trousers')
INSERT INTO #Test VALUES ('Trousers')
INSERT INTO #Test VALUES ('Trousers')
INSERT INTO #Test VALUES ('Trousers')
INSERT INTO #Test VALUES ('Shoes')
SELECT Product
,COUNT(1) AS Total
FROM #Test
GROUP BY Product
I need to put in my WHERE clause date ranges based on weeks Monday to Friday from different years, finding this one a bit tricky.
i.e
Monday to Friday
2016
06.06.16 – 10.06.16
13.06.16 – 17.06.16
2015
03.08.15 – 07.08.15
10.08.15 – 14.08.15
Try this
DECLARE #CurrentDate DATETIME = '2016.06.23'
DECLARE #CurrentMonday DATETIME = DATEADD(wk, DATEDIFF(wk,0,#CurrentDate), 0) -- 2016.06.20 MONDAY
DECLARE #CurrentFriday DATETIME = DATEADD(dd, 4, #CurrentMonday) -- 2016.06.24 FRIDAY
then you can use currentMonday and currentFriday in where clause.
E.g.
SELECT
*
FROM
YourTable
WHERE
date >= #CurrentMonday AND
date <= #CurrentFriday
DECLARE #Test TABLE
(
Product VARCHAR(100)
,DateRange Date
)
INSERT INTO #Test VALUES ('Hats', '20160610')
INSERT INTO #Test VALUES ('Hats','20160612')
INSERT INTO #Test VALUES ('Hats','20150804')
INSERT INTO #Test VALUES ('Hats','20150804')
INSERT INTO #Test VALUES ('Shirts','20150813')
INSERT INTO #Test VALUES ('Shirts','20150825')
INSERT INTO #Test VALUES ('Trousers','20150819')
INSERT INTO #Test VALUES ('Trousers','20150827')
INSERT INTO #Test VALUES ('Trousers','20150607')
INSERT INTO #Test VALUES ('Trousers','20150611')
INSERT INTO #Test VALUES ('Trousers','20150808')
INSERT INTO #Test VALUES ('Shoes','20150809')
SELECT
Product
,SUM(CASE WHEN DateRange BETWEEN '20150803' AND '20150807' THEN 1 ELSE 0 END) AS '03.08.15 – 07.08.15'
,SUM(CASE WHEN DateRange BETWEEN '20150810' AND '20150814' THEN 1 ELSE 0 END) AS '10.08.15 – 14.08.15'
,SUM(CASE WHEN DateRange BETWEEN '20160606' AND '20160610' THEN 1 ELSE 0 END) AS '06.06.16 – 10.06.16'
,SUM(CASE WHEN DateRange BETWEEN '20160613' AND '20160617' THEN 1 ELSE 0 END) AS '13.06.16 – 17.06.16'
FROM
#Test
WHERE Product IN (
'Hats'
,'Shirts'
,'Trousers'
,'Shoes'
)
GROUP BY Product
ORDER BY Product ASC

Rank data in TSQL

Data :
DECLARE #tblData TABLE (MediaID int,MediaTagName varchar(2000),MediaTypeName varchar(2000),keyitem varchar(2000),value varchar(2000))
INSERT INTO #tblData VALUES (48229,'Primary','Video','videoid','1234')
INSERT INTO #tblData VALUES (48229,'Primary','Video','src','somesrc')
INSERT INTO #tblData VALUES (48229,'Primary','Video','url','someurl')
INSERT INTO #tblData VALUES (48211,'Secondary','Video','videoid','1234')
INSERT INTO #tblData VALUES (48211,'Secondary','Video','src','somesrc')
INSERT INTO #tblData VALUES (48311,'Primary','Video','videoid','123456')
INSERT INTO #tblData VALUES (48311,'Primary','Video','src','somesrc')
INSERT INTO #tblData VALUES (48311,'Primary','Video','url','someurl')
Query tried :
SELECT
MediaID, MediaTagName,
RANK() OVER (PARTITION BY mediaid,mediatagname ORDER BY mediatagname) as rnk
FROM
#tblData
I need to get data that is partitioned based on MediaId and MedisTagName. essentially the end result should be :
DECLARE #tblDataExpected TABLE (MediaID int,MediaTagName varchar(2000),MediaTypeName varchar(2000),keyitem varchar(2000),value varchar(2000),rnk int)
INSERT INTO #tblDataExpected VALUES (48229,'Primary','Video','videoid','1234','1')
INSERT INTO #tblDataExpected VALUES (48229,'Primary','Video','src','somesrc','1')
INSERT INTO #tblDataExpected VALUES (48229,'Primary','Video','url','someurl','1')
INSERT INTO #tblDataExpected VALUES (48211,'Secondary','Video','videoid','1234','1')
INSERT INTO #tblDataExpected VALUES (48211,'Secondary','Video','src','somesrc','1')
INSERT INTO #tblDataExpected VALUES (48311,'Primary','Video','videoid','123456','2')
INSERT INTO #tblDataExpected VALUES (48311,'Primary','Video','src','somesrc','2')
INSERT INTO #tblDataExpected VALUES (48311,'Primary','Video','url','someurl','2')
SELECT * from #tblDataExpected
order by MediaID
Summary - The partition should be based off of mediatag and media id to generate the rank, however if there is a repeating mediaTag (for example "Primary") with a different media id, it should do an increment of the last rank it used for the primary with previous media ID and so on.
Firstly, you need to use DENSE_RANK instead of RANK as you have duplicates in your partitions. This will only increase the rank number on distinct rows.
Secondly, the partition only needs to be on MediaTagName as you are wanting to increment for every distinct MediaID for each MediaTagName. Therefore MediaID moves to the ORDER BY criteria.
I believe the following produces the results you are after:
SELECT td.MediaID,
td.MediaTagName,
td.MediaTypeName,
td.keyitem,
td.value,
DENSE_RANK() OVER (PARTITION BY MediaTagName ORDER BY MediaID) AS rnk
FROM #tblData AS td
ORDER BY td.MediaID;

If numeric then Insert numeric else Insert non-numeric

I have table source SRC such as:
*Column1*
First
Second
45
Fouth
Now I want to insert these data into table DEST (ID, NAME) with this logic:
If row is numeric, insert into (ID, NAME) VAUES (45, 'TBD').
If the row is not numeric, generate ID and insert into (ID, NAME) VALUES (*GENERATED ID*, NAME).
I tried something like:
DECLARE #i INT;
SELECT #i = MAX ( ID ) + 1
FROM DEST;
IF ( SELECT ISNUMERIC ( SELECT Column1 FROM SRC ) AS help ) = 1
BEGIN
INSERT INTO DEST (ID, NAME) VALUES (45, 'TBD')
END;
ELSE
BEGIN
INSERT INTO DEST (ID, NAME) SELECT ROW_NUMBER() OVER(ORDER BY NAME) +#i, 'First';
INSERT INTO DEST (ID, NAME) SELECT ROW_NUMBER() OVER(ORDER BY NAME) +#i, 'Second';
INSERT INTO DEST (ID, NAME) SELECT ROW_NUMBER() OVER(ORDER BY NAME) +#i, 'Fourth';
END;
(simplified solution to demonstrate the purpose, it should be dynamic, not hardcoded)
.., but that obviously does not work. How to do that?
One approach you can take is the following, which uses a CASE statement to allow you to differentiate between numeric and non-numeric values of Column1:
-- Some temporary tables to make the example work
CREATE TABLE #SRC (Column1 VARCHAR(50))
INSERT INTO #SRC (Column1) VALUES ('First'), ('Second'), ('45'), ('Fourth')
CREATE TABLE #DEST (ID INT)
DECLARE #i INT
-- If #DEST is empty we need to have an initial value of 1
SELECT #i = ISNULL(MAX(ID),0) + 1 FROM #DEST
PRINT #i
INSERT INTO #DEST (ID)
SELECT CASE ISNUMERIC(Column1)
WHEN 1 THEN Column1
ELSE ROW_NUMBER() OVER (ORDER BY Column1) + #i
END
FROM #SRC
SELECT *
FROM #DEST
DROP TABLE #SRC
DROP TABLE #DEST
The example will not port directly into your code, but should give you a good basis to begin working from in order to achieve your desired result.
Note my comment to your original post that you may get clashes on the ID column if inserting multiple rows with this. You will need to consider what to do in that situation.
You could try something like this.
First insert numeric values by checking ISNUMERIC.
INSERT INTO DEST (ID,Name)
SELECT TRY_CONVERT(INT,Column1),'TBD'
FROM SRC
WHERE ISNUMERIC(Column1) = 1
Now Insert other values
DECLARE #maxid INT
SELECT #maxid = MAX(ID) FROM DEST;
INSERT INTO DEST (ID,Name)
SELECT #maxid + ROW_NUMBER()OVER(ORDER BY Column1 ASC),Column1
FROM SRC
WHERE ISNUMERIC(Column1) = 0
Note : ISNUMERIC doesn't guarantee that the value will be converted successfully to a numeric value. Also it looks like you want to check if the value is integer and not numeric unless you are ok with truncation of decimal point. You can use Column1 LIKE '%[0-9]%' to check if a value contains only numbers and not decimal value if that is the case
For Example, the below value '.' returns ISNUMERIC as 1 however cannot be converted to a numeric or an int :
DECLARE #value varchar(10) = '.'
SELECT ISNUMERIC(#value),TRY_CONVERT(INT,#value),TRY_CONVERT(NUMERIC(18,2),#value)