Data Available in Table1
ID Name1 Address
1 nm1 abc
1 nm2 def
1 nm3 ghi
0 nm4 jkl
0 nm5 mno
0 nm6 pqr
1 nm7 stu
1 nm8 vwx
1 nm9 yza
0 nm10 bcd
Expected Output from Table1
ID Name1 Address Counter
1 nm1 abc 1
1 nm2 def 1
1 nm3 ghi 1
0 nm4 jkl 2
0 nm5 mno 2
0 nm6 pqr 2
1 nm7 stu 3
1 nm8 vwx 3
1 nm9 yza 3
0 nm10 bcd 4
Order must be sort using the key fields and need to generate the auto increment no in the expected output.
Thanks in advance.
You need to know how the rows are order. I suppose this is sample data and in your real scenario you can ordered the data in unique way. With your sample data I am using the following statement to order the rows:
CAST(REPLACE([Name], 'nm', '') AS INT)
There are different solutions of this (some depending on your SQL version, too). If you are using SQL Server 2012+ you can use the LAG function to find if ID value is changed from the previous and current row and then to sum this changes using OVER clause:
DECLARE #DataSource TABLE
(
[ID] INT
,[Name] VARCHAR(12)
,[Address] VARCHAR(12)
);
INSERT INTO #DataSource ([ID], [Name], [Address])
VALUES (1, 'nm1', 'abc')
,(1, 'nm2', 'def')
,(1, 'nm3', 'ghi')
,(0, 'nm4', 'jkl')
,(0, 'nm5', 'mno')
,(0, 'nm6', 'pqr')
,(1, 'nm7', 'stu')
,(1, 'nm8', 'vwx')
,(1, 'nm9', 'yza')
,(0, 'nm10', 'bcd');
SELECT *
,SUM([change_made]) OVER (ORDER BY CAST(REPLACE([Name], 'nm', '') AS INT))
FROM
(
SELECT *
,IIF([ID] <> LAG([ID], 1, -1) OVER (ORDER BY CAST(REPLACE([Name], 'nm', '') AS INT)), 1, 0) AS [change_made]
FROM #DataSource
) DS
ORDER BY CAST(REPLACE([Name], 'nm', '') AS INT);
Related
I have values within a Notes field consisting of values such as this where abc represents different words:
Request Notes: VAR - abc abc abc abc abc
Unfortunately the design of how the data is stored is very poor, and I need to pull out the Note Type for each "Required Notes:" record. It also needs ordering from most recent to oldest (last part of the string to first part of the string).
CREATE TABLE #TestData
(
ClientID int,
Notes varchar(8000)
)
insert into #TestData
(
ClientID,
Notes
)
select
1,
'Request Notes: VAR - abc abc abc abc abc'
union all
select
2,
'Request Notes: OZR - abc abc abc abc abc Request Notes: ACC - abc abc abc abc abc Request Notes: TYU - abc abc abc abc abc'
union all
select
3,
'Request Notes: TYU - abc abc abc abc abc Request Notes: VAR - abc abc abc abc abc'
This is how I expect the output to be for the above examples:
--Expected Output
Client ID Type Order
1 VAR 1
2 TYU 1
2 ACC 2
2 OZR 3
3 VAR 1
3 TYU 2
I have this together so far which extracts OZR, but I'm stumped on how to get the others and order the list into the expected output above.
DECLARE #Text varchar(500) = 'Request Notes: OZR - abc abc abc abc abc Request Notes: ACC - abc abc abc abc abc Request Notes: TYU - abc abc abc abc abc'
SELECT TRIM(REPLACE(REPLACE(SUBSTRING(#Text, CHARINDEX(':', #Text), CHARINDEX('-',#text) - CHARINDEX(':', #Text) + Len('-')),':',''),'-',''))
You can use openjson to extract your data as an array and filter:
select d.ClientId, n.*
from #testdata d
cross apply (
select
Left(j.[value],3) [Type],
Row_Number() over(order by Convert(int,j.[key]) desc) [Order]
from OpenJson(Concat('["',replace(notes,'Notes: ', '","'),'"]')) j
where j.[value] != 'Request'
)n;
Example Fiddle
Here's a recursive CTE version. It splits the string based on finding "Request Notes:" then does a left/right combo to extract the 3 letter code. It iterates the order as it builds. Then you select from the CTE and only take rows where there's some NotesRemainder:
;
WITH CTESplit
AS (
SELECT ClientID,
RIGHT(LEFT(Notes, CHARINDEX('Request Notes:', Notes) + 17), 3) AS NotesPart,
RIGHT(Notes, LEN(Notes) - CHARINDEX('Request Notes:', Notes) - 17) AS NotesRemainder,
-1 AS [Order]
FROM #TestData
WHERE Notes IS NOT NULL AND CHARINDEX('Request Notes:', Notes) > 0
UNION ALL
SELECT CTESplit.ClientID,
RIGHT(LEFT(CTESplit.NotesRemainder, CHARINDEX('Request Notes:', CTESplit.NotesRemainder) + 17), 3),
RIGHT(CTESplit.NotesRemainder, LEN(CTESplit.NotesRemainder) - CHARINDEX('Request Notes:', CTESplit.NotesRemainder)),
CTESplit.[Order] - 1
FROM CTESplit
WHERE CTESplit.NotesRemainder IS NOT NULL AND CHARINDEX('Request Notes:', CTESplit.NotesRemainder) > 0
UNION ALL
SELECT CTESplit.ClientID,
RIGHT(LEFT(CTESplit.NotesRemainder, CHARINDEX('Request Notes:', CTESplit.NotesRemainder) + 17), 3),
NULL,
CTESplit.[Order] - 1
FROM CTESplit
WHERE CTESplit.NotesRemainder IS NOT NULL AND CHARINDEX('Request Notes:', CTESplit.NotesRemainder) = 0
)
SELECT CS.ClientID,
CS.NotesPart AS Type,
CS.[Order] +(SELECT MIN([Order])*-1 FROM CTESplit WHERE ClientID = CS.ClientID) AS [Order]
FROM CTESplit AS CS
WHERE CS.NotesRemainder IS NOT NULL
ORDER BY CS.ClientID,
CS.[Order] ASC;
CREATE TABLE #Emp
(
ID int,
Name varchar(100)
)
INSERT INTO #Emp
VALUES (1, 'AAA'), (2, 'BBB'), (3, 'CCC')
CREATE TABLE #Task
(
EmpID int,
TaskName varchar(100),
[Hours] int
)
INSERT INTO #Task
VALUES (1, 'Task-1', 2), (1, 'Task-2', 4), (1, 'Task-5', 3),
(2, 'Task-3', 2), (2, 'Task-4', 4), (2, 'Task-5', 3),
(3, 'Task-1', 2), (3, 'Task-1', 4), (3, 'Task-1', 6),
(3, 'Task-2', 3), (3, 'Task-6', 1)
#Emp
ID Name
--------
1 AAA
2 BBB
3 CCC
#Task:
EmpID TaskName Hours
-------------------------
1 Task-1 2
1 Task-2 4
1 Task-5 3
2 Task-3 2
2 Task-4 4
2 Task-5 8
3 Task-1 2
3 Task-1 4
3 Task-1 6
3 Task-2 3
3 Task-6 1
For each employee I need to get the sum of hours for (Task-1 and Task-2) and for Task-5
Something like below
Name PrepHours(Task-1 + Task-2) ReviewHours(Task-5)
-------------------------------------------------------
AAA 6 3
BBB 0 8
CCC 15 0
I tried the query shown here, but it fails with error column #Task.TaskName is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause.
SELECT
Name, PrepHours, ReviewHours
FROM
#Emp AS E
JOIN
(SELECT
empid,
CASE
WHEN Taskname IN ('Task-1','Task-2')
THEN SUM(Hours)
ELSE 0
END AS 'PrepHours',
CASE
WHEN Taskname IN ('Task-5')
THEN SUM(Hours)
ELSE 0
END AS 'ReviewHours'
FROM
#Task
WHERE
Taskname IN ('Task-1', 'Task-2', 'Task-5')
GROUP BY
empid) AS t ON E.id = t.empid
ORDER BY
Name
So if I add the Taskname in the Group by it provides multiple rows for each. I need one row for each employee. Need help please.
Name PrepHours ReviewHours
-------------------------------
AAA 2 0
AAA 4 0
AAA 0 3
BBB 0 8
CCC 12 0
CCC 3 0
You can create your totals using a conditional case espression in a cross apply
select e.name, t.*
from #emp e
cross apply (
select
Sum(case when taskname in ('task-1','task-2') then hours else 0 end) PrepHours,
Sum(case when taskname ='Task-5' then hours else 0 end) ReviewHours
from #task t
where t.EmpId=e.Id
)t
You can make the original query work by moving the Sum outside the case statement:
SELECT [Name],
PrepHours,
ReviewHours
FROM #Emp AS E
JOIN (SELECT empid,
Sum( CASE
WHEN Taskname IN ( 'Task-1', 'Task-2' ) THEN [Hours]
ELSE 0
END) AS 'PrepHours',
sum(CASE
WHEN Taskname IN ( 'Task-5' ) THEN [Hours]
ELSE 0
END) AS 'ReviewHours'
FROM #Task
WHERE Taskname IN ( 'Task-1', 'Task-2', 'Task-5' )
GROUP BY empid) AS t
ON E.id = t.empid
ORDER BY Name
So I have 2 tables which iam joining using Inner Join.
Table 1 :
Name
Batch_Date
AcctID
Bob
18-08-11
32
Bob
19-08-11
32
Shawn
18-08-11
42
Shawn
20-08-11
42
Paul
18-08-11
36
Paul
19-08-11
36
Table 2
Code
order_Date
AcctID
1
18-08-11
32
0
NULL
32
0
NULL
42
0
NULL
42
1
18-08-11
36
1
18-08-11
36
So I want to get the name, last batch_date , AcctID from the table 1
and code, order date from table 2.
The challenge for me here is as there are multiple rows of same AcctId in table 2, if for any acctid, the date column is not null, I want to select that date and if date column is null for each row, I want to select the null value for date.
SO resulting dataset should look like below:
Name
Batch_Date
AcctID
Code
Order_Date
Bob
19-08-11
32
1
18-08-11
Shawn
20-08-11
42
0
NULL
Paul
19-08-11
36
1
18-08-11
OK, try this
--Set up your sample data in useable form, skip in your actual solution
with cteT1 as (
SELECT *
FROM (VALUES ('Bob', '18-08-11', 32), ('Bob', '19-08-11', 32)
, ('Shawn', '18-08-11', 42), ('Shawn', '20-08-11', 42)
, ('Paul', '18-08-11', 36), ('Paul', '19-08-11', 36)
) as T1 (CustName, BatchDate, AcctID)
), cteT2 as (
SELECT *
FROM (VALUES (1, '18-08-11', 32), (0, NULL, 32), (0, NULL, 42)
, (0, NULL, 42), (1, '18-08-11', 36), (1, '18-08-11', 36)
) as T2 (OrderCode, OrderDate, AcctID)
)
--Set up the solution - tag the newest of each table
, cteTopBatches as (
SELECT ROW_NUMBER() over (PARTITION BY AcctID ORDER BY BatchDate DESC) as BatchNewness, *
FROM cteT1
), cteTopOrders as (
SELECT ROW_NUMBER() over (PARTITION BY AcctID ORDER BY OrderDate DESC) as OrderNewness, *
FROM cteT2 --NOTE: NULLs sort below actual dates, but you could use COALESCE to force a specific value to use, probably in another CTE for readability
)
--Now combine the 2 tables keeping only the newest of each
SELECT T1.AcctID , T1.CustName , T1.BatchDate , T2.OrderCode , T2.OrderDate
FROM cteTopBatches as T1 INNER JOIN cteTopOrders as T2 ON T1.AcctID = T2.AcctID
WHERE T1.BatchNewness = 1 AND T2.OrderNewness = 1
I am trying to transpose column results in my table into row results. Here is the query that generates the table results:
CREATE TABLE Zone
([Zone] varchar(9), [CompanyID] int, [SubCount] int);
CREATE TABLE Company
([UniqueIdentifier]int, [Name] varchar(50));
--Adding Values into the table
INSERT INTO Company
([UniqueIdentifier], [Name])
VALUES
( 1, 'CompanyA'),
( 2, 'CompanyB'),
( 3, 'CompanyC'),
( 4, 'CompanyD'),
( 5, 'CompanyE');
--Adding Values to the table
INSERT INTO Zone
([Zone], [CompanyID], [SubCount])
VALUES
( 'Zone1', 1, 100),
( 'Zone2', 1, 200),
( 'Zone3', 2, 1250),
( 'Zone4', 3, 1440),
( 'Zone5', 4, 1445),
( 'Zone6', 4, 3250),
( 'Zone7', 5, 4440);
--Getting TOTALS
SELECT
CASE WHEN GROUPING(dbo.Company.Name)=1 THEN 'Grand Total' else dbo.Company.Name end as Company,
SUM(dbo.Zone.SubCount) as Subs
FROM dbo.Company INNER JOIN dbo.Zone ON dbo.Company.UniqueIdentifier = dbo.Zone.CompanyID
WHERE (dbo.Zone.SubCount IS NOT NULL) AND (dbo.Zone.SubCount > 0)
Group by ROLLUP(dbo.Company.Name)
ORDER BY Subs DESC;
HERE ARE THE RESULTS OF THE QUERY:
Company | Subs
------------------------
1 Grand Total | 12125
2 CompanyD | 4695
3 CompanyE | 4440
4 CompanyC | 1440
5 CompanyB | 1250
6 CompanyA | 300
DESIRED RESULT WOULD LOOK LIKE:
Company |CompanyA|CompanyB|CompanyC|CompanyE|CompanyD|Grand Total
---------------------------------------------------------------------
Subs | 300 | 1250 | 1440 | 4440 | 4695 | 12125
ANY HELP IS GREATLY APPRECIATED!
All you need to do is use pivot function for converting these rows into columns
SELECT 'Subs' AS Company
,[CompanyA]
,[CompanyB]
,[CompanyC]
,[CompanyD]
,[CompanyE]
,[Grand Total]
FROM (
SELECT CASE
WHEN GROUPING(dbo.Company.NAME) = 1
THEN 'Grand Total'
ELSE dbo.Company.NAME
END AS Company
,SUM(dbo.Zone.SubCount) AS Subs
FROM dbo.Company
INNER JOIN dbo.Zone ON dbo.Company.UNIQUEIDENTIFIER = dbo.Zone.CompanyID
WHERE (dbo.Zone.SubCount IS NOT NULL)
AND (dbo.Zone.SubCount > 0)
GROUP BY ROLLUP(dbo.Company.NAME)
) a
pivot(max(subs) FOR Company IN (
[CompanyA]
,[CompanyB]
,[CompanyC]
,[CompanyD]
,[CompanyE]
,[Grand Total]
)) piv;
I have a sales table like this
salesmanID customerID productID date
1 100 1 20.02.2015
1 101 2 20.02.2015
1 102 4 21.02.2015
2 122 6 20.02.2015
2 120 2 22.02.2015
3 150 1 23.02.2015
3 144 8 23.02.2015
3 122 4 24.02.2015
and I want to count of daily sales for each salesman day by day between min and max dates of given two dates(in this sample 20-02-2015 and 24-02-2015) like this,
salesmanID 20.02.2015 21.02.2015 22.02.2015 23.02.2015 24.02.2015
1 2 1 0 0 0
2 1 0 1 0 0
3 0 0 0 2 1
How can I do this in SQL query?
you have to use PIVOT and count of SalesmanID
declare #temp table
(salesmanID int, customerID int, productID int, dates VARCHAR(10))
insert into #temp values (1,100,1,CONVERT(VARCHAR(10), '20.02.2015', 104) )
insert into #temp values (1,101,2,CONVERT(VARCHAR(10), '20.02.2015', 104) )
insert into #temp values (1,102,4,CONVERT(VARCHAR(10), '21.02.2015', 104) )
insert into #temp values (2,122,6,CONVERT(VARCHAR(10), '20.02.2015', 104) )
insert into #temp values (2,120,2,CONVERT(VARCHAR(10), '22.02.2015', 104) )
insert into #temp values (3,150,1,CONVERT(VARCHAR(10), '23.02.2015', 104) )
insert into #temp values (3,144,8,CONVERT(VARCHAR(10), '23.02.2015', 104) )
insert into #temp values (3,122,4,CONVERT(VARCHAR(10), '24.02.2015', 104) )
--QUERY
SELECT *
FROM (SELECT salesmanID, salesmanID as id,dates
FROM #temp) as s
PIVOT
(
count(id)
FOR dates IN ([20.02.2015], [21.02.2015], [22.02.2015], [23.02.2015], [24.02.2015])
)AS pivots
RESULT