T-SQL Date week day date ranges - tsql

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

Related

tSQL: Increase every group by 1

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.

How to order by result set in union query in T-SQL

I want to use order by clause in my last sql query and I have more than 3 union queries. I do not want to order the top 2 union query but I want to use order by clause in my last sql statement.
Currently, getting error
ORDER BY items must appear in the select list if the statement contains a UNION, INTERSECT or EXCEPT operator.
select 'Total Number of pat' Name, convert(varchar(20), count(id)) Number from table2 where id = 5
union
select 'Total Number of Doc' Name, convert(varchar(20), count(id)) Number from table3
union
select x.usertype, count(distinct userid) cnt
from [dbo].table1 t
cross apply (values (
case when t.userid like '%[0-9][0-9[0-9]' then 'transition' else 'non transition' end,
t.userid
)) x(usertype, userid)
where t.date >= dateadd(day,-7, getdate())
group by x.usertype
order by usertype desc
order by is sorting the result of the unions all together however you can introduce a orderIndex column for imlementing the right ordering.
Here the sample:
I've tried to build sample data in the following code.
create table table1(
userid varchar(100),
usertype varchar(100),
date date
)
insert into table1(userid, date) values ('Einsmayr', '2020-10-27')
insert into table1(userid, date) values ('Eins123', '2020-10-27')
insert into table1(userid, date) values ('Einschmid', '2020-10-27')
insert into table1(userid, date) values ('Einshuber', '2020-10-27')
insert into table1(userid, date) values ('Einsreitmayr', '2020-10-27')
create table table2 (
Name varchar(100),
id int
)
insert into table2(Name, id) values('Zweirich', 5)
insert into table2(Name, id) values('Zweifel', 6)
create table table3 (
Name varchar(100),
id int
)
insert into table3(Name, id) values('Dreisinger', 17)
insert into table3(Name, id) values('Dreibert', 18)
This allows the following queries:
select usertype, Number
from (
select 'Total Number of pat' usertype, convert(varchar(20), count(id)) Number, 1 orderIndex from table2 where id = 5
union
select 'Total Number of Doc' Name, convert(varchar(20), count(id)) Number, 2 orderIndex from table3
union
select usertype, count(distinct userid) Number, 3 orderIndex
from (
select userid, case when userid like '%[0-9][0-9[0-9]' then 'transition' else 'non transition' end usertype
from table1
where date >= dateadd(day,-7, getdate())
) x
group by x.usertype
) y
order by y.orderIndex, y.usertype
Find the solution here: https://dbfiddle.uk/?rdbms=sqlserver_2019&fiddle=ac396c48f5dbcb4a53ad40fac70e9236

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

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)

TSQL: Remove duplicates based on max(date)

I am searching for a query to select the maximum date (a datetime column) and keep its id and row_id. The desire is to DELETE the rows in the source table.
Source Data
id date row_id(unique)
1 11/11/2009 1
1 12/11/2009 2
1 13/11/2009 3
2 1/11/2009 4
Expected Survivors
1 13/11/2009 3
2 1/11/2009 4
What query would I need to achieve the results I am looking for?
Tested on PostgreSQL:
delete from table where (id, date) not in (select id, max(date) from table group by id);
There are various ways of doing this, but the basic idea is the same:
- Indentify the rows you want to keep
- Compare each row in your table to the ones you want to keep
- Delete any that don't match
DELETE
[source]
FROM
yourTable AS [source]
LEFT JOIN
yourTable AS [keep]
ON [keep].id = [source].id
AND [keep].date = (SELECT MAX(date) FROM yourTable WHERE id = [keep].id)
WHERE
[keep].id IS NULL
DELETE
[yourTable]
FROM
[yourTable]
LEFT JOIN
(
SELECT id, MAX(date) AS date FROM yourTable GROUP BY id
)
AS [keep]
ON [keep].id = [yourTable].id
AND [keep].date = [yourTable].date
WHERE
[keep].id IS NULL
DELETE
[source]
FROM
yourTable AS [source]
WHERE
[source].row_id != (SELECT TOP 1 row_id FROM yourTable WHERE id = [source].id ORDER BY date DESC)
DELETE
[source]
FROM
yourTable AS [source]
WHERE
NOT EXISTS (SELECT id FROM yourTable GROUP BY id HAVING id = [source].id AND MAX(date) != [source].date)
Because you are using SQL Server 2000, you'er not able to use the Row Over technique of setting up a sequence and to identify the top row for each unique id.
So, your proposed technique is to use a datetime column to get the top 1 row to remove duplicates. That might work, but there is a possibility that you might still get duplicates having the same datetime value. But that's easy enough to check for.
First check the assumption that all rows are unique based on the id and date columns:
CREATE TABLE #TestTable (rowid INT IDENTITY(1,1), thisid INT, thisdate DATETIME)
INSERT INTO #TestTable (thisid,thisdate) VALUES (1, '11/11/2009')
INSERT INTO #TestTable (thisid,thisdate) VALUES (1, '12/11/2009')
INSERT INTO #TestTable (thisid,thisdate) VALUES (1, '12/12/2009')
INSERT INTO #TestTable (thisid,thisdate) VALUES (2, '1/11/2009')
INSERT INTO #TestTable (thisid,thisdate) VALUES (2, '1/11/2009')
SELECT COUNT(*) AS thiscount
FROM #TestTable
GROUP BY thisid, thisdate
HAVING COUNT(*) > 1
This example returns a value of 2 - indicating that you will still end up with duplicates even after using the date column to remove duplicates. If you return 0, then you have proven that your proposed technique will work.
When de-duping production data, I think one should take some precautions and test before and after. You should create a table to hold the rows you plan to remove so you can recover them easily if you need to after the delete statement has been executed.
Also, it's a good idea to know beforehand how many rows you plan to remove so you can verify the count before and after - and you can gauge the magnitude of the delete operation. Based on how many rows will be affected, you can plan when to run the operation.
To test before the de-duping process, find the occurrences.
-- Get occurrences of duplicates
SELECT COUNT(*) AS thiscount
FROM
#TestTable
GROUP BY thisid
HAVING COUNT(*) > 1
ORDER BY thisid
That gives you the rows with more than one row with the same id. Capture the rows from this query into a temporary table and then run a query using the SUM to get the total number of rows that are not unique based on your key.
To get the number of rows you plan to delete, you need the count of rows that are duplicate based on your unique key, and the number of distinct rows based on your unique key. You subtract the distinct rows from the count of occurrences. All that is pretty straightforward - so I'll leave you to it.
Try this
declare #t table (id int, dt DATETIME,rowid INT IDENTITY(1,1))
INSERT INTO #t (id,dt) VALUES (1, '11/11/2009')
INSERT INTO #t (id,dt) VALUES (1, '11/12/2009')
INSERT INTO #t (id,dt) VALUES (1, '11/13/2009')
INSERT INTO #t (id,dt) VALUES (2, '11/01/2009')
Query:
delete from #t where rowid not in(
select t.rowid from #t t
inner join(
select MAX(dt)maxdate
from #t
group by id) X
on t.dt = X.maxdate )
select * from #t
Output:
id dt rowid
1 2009-11-13 00:00:00.000 3
2 2009-11-01 00:00:00.000 4
delete from temp where row_id not in (
select t.row_id from temp t
right join
(select id,MAX(dt) as dt from temp group by id) d
on t.dt = d.dt and t.id = d.id)
I have tested this answer..
INSERT INTO #t (id,dt) VALUES (1, '11/11/2009')
INSERT INTO #t (id,dt) VALUES (1, '11/12/2009')
INSERT INTO #t (id,dt) VALUES (1, '11/13/2009')
INSERT INTO #t (id,dt) VALUES (2, '11/01/2009')
select * from #t
;WITH T AS(
select dense_rank() over(partition by id order by dt desc)NO,DT,ID,rowid from #t )
DELETE T WHERE NO>1