combine row for each Unique value based on datetime - sql-server

I have a table with three column
TICKET_ID ASSIGN ASSIGN_DATE
5692 ASSIGN-5 2013-07-17 19:37:09.000
5740 ASSIGN-5 2013-07-17 19:37:09.000
5741 ASSIGN-5 2013-07-17 19:37:09.000
5742 ASSIGN-5 2013-07-17 10:40:15.000
5742 ASSIGN-4 2013-07-17 19:37:09.000
I need to combine ASSIGN row to one row for each TICKET_ID based on datetime ascending result should be like this
TICKET_ID ASSIGN
5692 ASSIGN-5
5740 ASSIGN-5
5741 ASSIGN-5
5742 ASSIGN-4 ASSIGN-5
how can I build the result ?

You can use FOR XML PATH('')
WITH SampleData(TICKET_ID, ASSIGN, ASSIGN_DATE) AS(
SELECT 5692, 'ASSIGN-5', CAST('2013-07-17 19:37:09.000' AS DATETIME) UNION ALL
SELECT 5740, 'ASSIGN-5', '2013-07-17 19:37:09.000' UNION ALL
SELECT 5741, 'ASSIGN-5', '2013-07-17 19:37:09.000' UNION ALL
SELECT 5742, 'ASSIGN-5', '2013-07-17 10:40:15.000' UNION ALL
SELECT 5742, 'ASSIGN-4', '2013-07-17 19:37:09.000'
)
SELECT
TICKET_ID,
ASSIGN = STUFF((
SELECT ' ' + ASSIGN
FROM SampleData
WHERE TICKET_ID = sd.TICKET_ID
ORDER BY ASSIGN_DATE ASC
FOR XML PATH(''), TYPE).value('.', 'VARCHAR(MAX)'
), 1, 1, '')
FROM SampleData sd
GROUP BY sd.TICKET_ID
Read this article by Aaron Bertrand for more details.

Here is the Required Result Set .. given two solution first is simply join two table on the basis of ticket id , 2nd join on the basis of Assign row it will give 13 rows
----------1st-------------------
Select T.*,A.* from Tikets T with(nolock)
join
Assignement A with(nolock)
on a.TICKET_ID = t.TICKET_ID
order by t.ASSIGN_DATE
-----------2nd-----------------
Select T.*,A.* from Tikets T with(nolock)
join
Assignement A with(nolock)
on a.ASSIGN like t.ASSIGN +'%'
order by t.ASSIGN_DATE
enter link description here
Thanks

Related

How to SUM() appropriate rows in MSSQL query for one column with INNER JOIN?

I have a MSSQL query:
SELECT Artikel.ArtikelID, Artikel.K_HerstellerName AS ManufacturerName, Artikel.K_HerstellerPN AS ManufacturerPN, Artikel.ArtikelNummer AS SupplierPN, Artikel.Bezeichnung + ' - ' + Artikel.LangText AS Description,
Artikel.LetzterEK AS Price, Artikel.IstGesperrt AS Gesperrt, Lager.Verfuegbar AS Quantity, Lager.LagerPlatz AS LP
FROM Artikel INNER JOIN
Lager ON Artikel.ArtikelID = Lager.ArtikelID
WHERE (Artikel.K_HerstellerPN <> '') AND (Artikel.IstGesperrt = 'False') AND (Artikel.ArtikelNummer LIKE '%[0-9]-[0-9]%')
Here you can see how it looks like: https://ibb.co/fyOHv9
How to SUM() appropriate rows in MSSQL query for column "Quantity"?
In result all rows must be unique.
you need to group by all the columns that make each row unique, then use aggregate functions in your select, for example:
SELECT
MIN(Artikel.ArtikelID ) ArtikelID,
MIN(Artikel.K_HerstellerName) ManufacturerName,
SUM(Lager.Verfuegbar) Quantity
FROM Artikel
INNER JOIN Lager ON Artikel.ArtikelID = Lager.ArtikelID
WHERE (Artikel.K_HerstellerPN <> '') ...
GROUP BY
Artikel.ArtikelID,
Artikel.K_HerstellerName,
...

Unable concate NULL value in SQL using CONCAT, COALESCE and ISNULL

I have a query with multiple joins where I want to combine records from two columns into one. If one column is empty then I want to show one column value as result. I tried with CONCAT, COALEASE and ISNULL but no luck. What am I missing here?
My objective is, create one column which has combination of s.Script AS Original and FromAnotherTable from query. Below query runs but throws Invalid column name 'Original' and Invalid column name 'FromAnotherTable'. when I try to use CONCAT, COALEASE or ISNULL .
SQL Query:
SELECT DISTINCT
c.Name AS CallCenter,
LTRIM(RTRIM(s.Name)) Name,
d.DNIS,
s.ScriptId,
s.Script AS Original,
(
SELECT TOP 5 CCSL.Line+'; '
FROM CallCenterScriptLine CCSL
WHERE CCSL.ScriptId = s.ScriptId
ORDER BY ScriptLineId FOR XML PATH('')
) AS FromAnotherTable,
--CONCAT(s.Script, SELECT TOP 5 CCSL.Line+'; ' FROM dbo.CallCenterScriptLine ccsl WHERE ccsl.ScriptId = s.ScriptId ORDER BY ccsl.ScriptLineId xml path(''))
--CONCAT(Original, FromAnotherTable) AS Option1,
--COALESCE(Original, '') + FromAnotherTable AS Option2,
--ISNULL(Original, '') + FromAnotherTable AS Option3,,
r.UnitName AS Store,
r.UnitNumber
FROM CallCenterScript s WITH (NOLOCK)
INNER JOIN CallCenterDNIS d WITH (NOLOCK) ON d.ScriptId = s.ScriptId
INNER JOIN CallCenter c WITH (NOLOCK) ON c.Id = s.CallCenterId
INNER JOIN CallCenterDNISRestaurant ccd WITH (NOLOCK) ON ccd.CallCenterDNISId = d.CallCenterDNISId
INNER JOIN dbo.Restaurant r WITH (NOLOCK) ON r.RestaurantID = ccd.CallCenterRestaurantId
WHERE c.Id = 5
AND (1 = 1)
AND (s.IsDeleted = 0 OR s.IsDeleted IS NULL)
ORDER BY DNIS ASC;
Output:
This works:
DECLARE #Column1 VARCHAR(50) = 'Foo',
#Column2 VARCHAR(50) = NULL;
SELECT CONCAT(#Column1,#Column2);
SELECT COALESCE(#Column2, '') + #Column1
SELECT ISNULL(#Column2, '') + #Column1
So I am not sure what I am missing in my original query.
Look at row 3 in the results you are getting. In your concatenated columns (Option1, 2, 3) you are getting the first script column twice. Not the first one + the second one like you expect.
The reason is because you've aliased your subquery "script" which is the same name as another column in your query, which makes it ambiguous.
Change the alias of the subquery and the problem should go away. I'm frankly surprised your query didn't raise an error.
EDIT: You can't use a column alias in another column's definition in the same level of the query. In other words, you can't do this:
SELECT
SomeColumn AS A
, (Subquery that returns a column) AS B
, A + B --this is not allowed
FROM ...
You can either create a CTE that returns the aliased columns and then concatenate them in the main query that selects from the CTE, or you have to use the original sources of the aliases, like so:
SELECT
SomeColumn AS A
, (Subquery that returns a column) AS B
, SomeColumn + (Subquery that returns a column) --this is fine
FROM ...
I took another approach where instead on creating separate column, I used ISNULL in my subQuery which returns my desired result.
Query:
SELECT DISTINCT
c.Name AS CallCenter,
LTRIM(RTRIM(s.Name)) Name,
d.DNIS,
s.ScriptId,
s.Script AS Original,
(
SELECT TOP 5 ISNULL(CCSL.Line, '')+'; ' + ISNULL(s.Script, '')
FROM CallCenterScriptLine CCSL
WHERE CCSL.ScriptId = s.ScriptId
ORDER BY ScriptLineId FOR XML PATH('')
) AS FromAnotherTable,
r.UnitName AS Store,
r.UnitNumber
FROM CallCenterScript s WITH (NOLOCK)
INNER JOIN CallCenterDNIS d WITH (NOLOCK) ON d.ScriptId = s.ScriptId
INNER JOIN CallCenter c WITH (NOLOCK) ON c.Id = s.CallCenterId
INNER JOIN CallCenterDNISRestaurant ccd WITH (NOLOCK) ON ccd.CallCenterDNISId = d.CallCenterDNISId
INNER JOIN dbo.Restaurant r WITH (NOLOCK) ON r.RestaurantID = ccd.CallCenterRestaurantId
WHERE c.Id = 5
AND (1 = 1)
AND (s.IsDeleted = 0 OR s.IsDeleted IS NULL)
ORDER BY DNIS ASC;
Here's a simplified example using table variables.
Instead of using a subquery for a field, it uses a CROSS APPLY.
And CONCAT in combination with STUFF is used to glue the strings together.
declare #Foo table (fooID int identity(1,1) primary key, Script varchar(30));
declare #Bar table (barID int identity(1,1) primary key, fooID int, Line varchar(30));
insert into #Foo (Script) values
('Test1'),('Test2'),(NULL);
insert into #Bar (fooID, Line) values
(1,'X'),(1,'Y'),(2,NULL),(3,'X'),(3,'Y');
select
f.fooID,
f.Script,
x.Lines,
CONCAT(Script+'; ', STUFF(x.Lines,1,2,'')) as NewScript
from #Foo f
cross apply (
select '; '+b.Line
from #Bar b
where b.fooID = f.fooID
FOR XML PATH('')
) x(Lines)
Result:
fooID Script Lines NewScript
----- ------- ------- -----------
1 Test1 ; X; Y Test1; X; Y
2 Test2 NULL Test2;
3 NULL ; X; Y X; Y

How to use the current select column value in a column sub select in SQL

I have a query that for each row, adds a comma seperated string to one column using a sub query. So for example, a work order may be attached to many parts. I do my regular select, but use the SQL function 'STUFF' to build the 'Parts column. That way in the results, the arts column has multiple entries. So i do i do the sub query for the column i use 'STUFF" on to get all parts using the main column id of the base query? This is a little weird for me to explain but this snippet will help you understand what i am trying to accomplish:
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
SET FMTONLY OFF;
IF OBJECT_ID('tempdb..#TEMP_KikusuiRpt') IS NOT NULL DROP TABLE #TEMP_KikusuiRpt
-- Get the current date to work on if they do not send in date params. Meaning it is automated run --
-- if hey do send in date ranges, then it is manual run
IF NULLIF(#StartDate, '') IS NULL
BEGIN
-- Set start date param to the first day of the current month
SET #StartDate = DATEADD(mm, DATEDIFF(mm,0,GETDATE()), 0)
END
IF NULLIF(#EndDate, '') IS NULL
BEGIN
-- Set end day to the last day what ever that may be of the current month.
SET #EndDate = DATEADD(s,-1,DATEADD(mm, DATEDIFF(m,0,GETDATE())+1,0))
END
SELECT
manufacturer.mfrname AS Manufacturer,
model.modelnumber AS BaseModelNumber,
control.controlserialnumber AS Serial,
Customer.CustCompany,
Control.ControlDateAdded as Received,
Control.ControlCalDate as 'Completed (Cal Date)',
WorkOrder.WorkOrderID as woid,
Receiver.ReceiverID,
WorkOrderComments.CommentRcvr as 'Receiver Comments',
WorkOrderCOmments.CommentRpt as 'Report Comments',
ISNULL(STUFF((SELECT ', ' + PartID
FROM WorkOrderPart WOP
INNER JOIN WorkORder WO ON WOP.WorkOrderID = WO.WorkOrderID
INNER JOIN Model ON Model.ModelID = WO.ModelID
WHERE WO.MfrCode IN (SELECT MfrCode FROM Manufacturer where MfrName LIKE '%Kikusui%')
--
-- Right here i need somehow to do this
--
-- the workorder on the stuff query needs to be attached to the base select work order row instance, or else i get all parts in each row not just for the work order
where wo.WorkOrderID = WOID
FOR XML PATH('')
), 1, 1, ''), 'NA') AS Parts
INTO #TEMP_KikusuiRpt
FROM WorkOrder
INNER JOIN Control ON WorkOrder.ControlNumber = Control.ControlNumber
INNER JOIN model ON control.modelid = model.modelid
INNER JOIN manufacturer on model.mfrcode = manufacturer.mfrcode
INNER JOIN WorkOrderComments ON WorkOrderComments.WorkOrderID = WorkOrder.WorkOrderID
INNER JOIN Receiver ON Receiver.ReceiverID = WorkOrder.ReceiverID
INNER JOIN Customer ON Customer.CustID = WorkOrder.CustID
WHERE Manufacturer.MfrName LIKE '%Kikusui%'
AND #StartDate > WorkOrder.DateReceived
AND #EndDate < WorkOrder.DateCompleted
SELECT * FROM #TEMP_KikusuiRpt
DROP TABLE #TEMP_KikusuiRpt
So hopefully i have explained this clearly enough. Is there a way to handle this? The reason being that my Parts column is populated, but not filtered enough to the model that is returned.
You just need to alias your outer and inner model table references. Here's what the main select would look like:
SELECT
manufacturer.mfrname AS Manufacturer,
m1.modelnumber AS BaseModelNumber,
control.controlserialnumber AS Serial,
Customer.CustCompany,
Control.ControlDateAdded as Received,
Control.ControlCalDate as 'Completed (Cal Date)',
WorkOrder.WorkOrderID as woid,
Receiver.ReceiverID,
WorkOrderComments.CommentRcvr as 'Receiver Comments',
WorkOrderCOmments.CommentRpt as 'Report Comments',
ISNULL(STUFF((SELECT ', ' + PartID
FROM WorkOrderPart WOP
INNER JOIN WorkORder WO ON WOP.WorkOrderID = WO.WorkOrderID
INNER JOIN Model m2 ON m2.ModelID = WO.ModelID
WHERE WO.MfrCode IN (SELECT MfrCode FROM Manufacturer where MfrName LIKE '%Kikusui%')
AND m1.modelnumber = m2.modelnumber
FOR XML PATH('')
), 1, 1, ''), 'NA') AS Parts
INTO #TEMP_KikusuiRpt
FROM WorkOrder
INNER JOIN Control ON WorkOrder.ControlNumber = Control.ControlNumber
INNER JOIN model m1 ON control.modelid = m1.modelid
INNER JOIN manufacturer on m1.mfrcode = manufacturer.mfrcode
INNER JOIN WorkOrderComments ON WorkOrderComments.WorkOrderID = WorkOrder.WorkOrderID
INNER JOIN Receiver ON Receiver.ReceiverID = WorkOrder.ReceiverID
INNER JOIN Customer ON Customer.CustID = WorkOrder.CustID
WHERE Manufacturer.MfrName LIKE '%Kikusui%'
AND #StartDate > WorkOrder.DateReceived
AND #EndDate < WorkOrder.DateCompleted

Create View - Declare a variable

I am creating a view that is using that STUFF function. I want to put the result of STUFF in a variable for my view. The problem I am having is declaring my variable. It gives me the message "Incorrect Syntax near 'DECLARE'. Expecting '(' or SELECT." I already have the '(' in there. I have tried putting a BEGIN before it. I have tried putting it after the SELECT word. But nothing seems to work and I cannot find a solution in my search. I am using SQL Server 2012
CREATE VIEW [AQB_OB].[GISREQUESTEDBURNS]
AS
(DECLARE #CONDITIONS AS varchar(20)
SET #CONDITIONS = (SELECT DISTINCT BD.[RequestedBurnsID]
,[ConditionsReasonsID] = STUFF((SELECT ', ' + CONVERT(VARCHAR (20),[ConditionsReasonsID]) FROM [AQB_OB].[BurnDecisions] WHERE [RequestedBurnsID]= BD.[RequestedBurnsID] ORDER BY [RequestedBurnsID] ASC
FOR XML PATH ('')) , 1 , 1, '') FROM
[AQB_OB].[BurnDecisions] BD)
SELECT RB.[RequestedBurnsID] AS REQUESTEDBURNID
,BUY.[BurnYear] AS BURNYEAR
,CY.[CurrentYear] AS CURRENTYEAR
,RB.[BurnSitesID] AS BURNSITESID
,[BurnerID] AS BURNERID
,[Contact] AS CONTACT
,[BurnDecision] AS BURNDECISION
,RB.[Comment] AS COMMENT
,#CONDITIONS AS CONDITIONS
FROM [AQB_MON].[AQB_OB].[RequestedBurns] RB
LEFT join AQB_MON.[AQB_OB].[PileDryness] PD on RB.[PileDrynessID] = PD.[PileDrynessID]
inner join AQB_MON.[AQB_OB].[BurnYear] BUY on BUY.BurnYearID = BP.BurnYearID
inner join AQB_MON.[AQB_OB].[CurrentYear] CY on CY.CurrentYearID = BUY.CurrentYearID
GO
You can't declare variables in a view. Could you make it into a function or stored procedure?
Edit - you might also be able to put something into a CTE (Common Table Expression) and keep it as a view.
e.g.
WITH conditions as
(
... do the STUFF here
)
SELECT blah
FROM blah
INNER JOIN conditions
(or CROSS JOIN conditions if its just one row, I can't quite decipher what your data is like)
Here is a sample query that uses a CTE (Common Table Expression) to nicely emulate internal variable construction, as described by James Casey. You can test-run it in your version of SQL Server.
CREATE VIEW vwImportant_Users AS
WITH params AS (
SELECT
varType='%Admin%',
varMinStatus=1)
SELECT status, name
FROM sys.sysusers, params
WHERE status > varMinStatus OR name LIKE varType
SELECT * FROM vwImportant_Users
yielding output:
status name
12 dbo
0 db_accessadmin
0 db_securityadmin
0 db_ddladmin
also via JOIN
WITH params AS ( SELECT varType='%Admin%', varMinStatus=1)
SELECT status, name
FROM sys.sysusers INNER JOIN params ON 1=1
WHERE status > varMinStatus OR name LIKE varType
also via CROSS APPLY
WITH params AS ( SELECT varType='%Admin%', varMinStatus=1)
SELECT status, name
FROM sys.sysusers CROSS APPLY params
WHERE status > varMinStatus OR name LIKE varType
Or use a CTE (common table expression) as subselect like:
WITH CTE_Time(Clock)
AS(
SELECT 11 AS [Clock] -- set var
)
SELECT
DATEPART(HOUR, GETDATE()) AS 'actual hour',
CASE
WHEN DATEPART(HOUR, GETDATE()) >= (SELECT [Clock] FROM CTE_Time) THEN 'after'
ELSE 'before'
END AS [Data]
Try put the condition subquery directly inside the the view select statement. you may CAST the XML to VARCHAR(20).
CREATE VIEW [AQB_OB].[GISREQUESTEDBURNS]
AS
SELECT RB.[RequestedBurnsID] AS REQUESTEDBURNID
,BUY.[BurnYear] AS BURNYEAR
,CY.[CurrentYear] AS CURRENTYEAR
,RB.[BurnSitesID] AS BURNSITESID
,[BurnerID] AS BURNERID
,[Contact] AS CONTACT
,[BurnDecision] AS BURNDECISION
,RB.[Comment] AS COMMENT,
(
SELECT DISTINCT BD.[RequestedBurnsID],
[ConditionsReasonsID] = STUFF((SELECT ', ' + CONVERT(VARCHAR (20), [ConditionsReasonsID]) FROM [AQB_OB].[BurnDecisions]
WHERE [RequestedBurnsID]= BD.[RequestedBurnsID] ORDER BY [RequestedBurnsID] ASC
FOR XML PATH ('')) , 1 , 1, '') FROM
[AQB_OB].[BurnDecisions] BD
) AS CONDITIONS
FROM [AQB_MON].[AQB_OB].[RequestedBurns] RB
LEFT join AQB_MON.[AQB_OB].[PileDryness] PD on RB.[PileDrynessID] = PD.[PileDrynessID]
inner join AQB_MON.[AQB_OB].[BurnYear] BUY on BUY.BurnYearID = BP.BurnYearID
inner join AQB_MON.[AQB_OB].[CurrentYear] CY on CY.CurrentYearID = BUY.CurrentYearID

Convert groups of multiple key-value rows to XML

I have a table called userInfo that has data similar to the following:
Id, Field, Value
---------------------
1, FirstName, John
1, LastName, Smith
1, Age, 25
1, Gender, Male
2, FirstName, Jane
2, LastName, Smythe
2, Age, 24
2, Gender, Female
What I need is some T-SQL that will produce a single row for each Id with the following structure:
Row:1
<FieldValues>
<FirstName>John</FirstName>
<LastName>Smith</LastName>
<Age>25</Age>
<Gender>Male</Gender>
</FieldValues>
Row:2
<FieldValues>
<FirstName>Jane</FirstName>
<LastName>Smythe</LastName>
<Age>24</Age>
<Gender>Female</Gender>
</FieldValues>
I have tried a couple of things to get this but can't get figure this out.
Edit:
The list of Fields I provided here (i.e. FirstName, LastName, etc) is not a static list of fields. I will be adding and taking away from this list all the time so the query would be able to handle this automatically). Ideally I could use something like FOR XML PATH('FieldValues')
You can build your XML as a string using for xml path('') and then cast to XML.
select T.Id,
cast('<FieldValues>' + (
select '<'+T2.Field+'>'+
(select T2.Value as '*' for xml path(''))+
'</'+T2.Field+'>'
from dbo.YourTable as T2
where T.Id = T2.Id
for xml path(''), type
).value('text()[1]', 'varchar(max)') +
'</FieldValues>' as xml) as FieldValues
from dbo.YourTable as T
group by T.Id;
SQL Fiddle
This part (select T2.Value as '*' for xml path('')) is there to take care of characters that needs to be entities in the value like &.
Here is one way:
SELECT '<FieldValues>'+
'<FirstName>'+fn.Value +'</FirstName>' +
'<LastName>'+ln.Value +'</LastName>' +
'<Age>'+age.Value +'</Age>' +
'<Gender>'+gender.Value +'</Gender>' +
'</FieldValues>
FROM (SELECT DISTINCT ID FROM userInfo) t
JOIN userInfo fn ON t.ID = fn.ID and fn.Field = 'FirstName'
JOIN userInfo ln ON t.ID = ln.ID and ln.Field = 'LastName'
JOIN userInfo age ON t.ID = age.ID and age.Field = 'Age'
JOIN userInfo gender ON t.ID = gender.ID and gender.Field = 'Gender'
How this works:
First I create table of just the unique ID numbers.
SELECT DISTINCT ID FROM table
Then I use this table to join back to the main table for each field. (Each of these joins will have only one row per ID.)
JOIN table fn ON t.ID = fn.ID and fn.Field = 'FirstName'
JOIN table ln ON t.ID = ln.ID and ln.Field = 'LastName'
JOIN table age ON t.ID = age.ID and age.Field = 'Age'
JOIN table gender ON t.ID = gender.ID and gender.Field = 'Gender'
Finally I create a string formatted as you need.
'<FieldValues>'+
'<FirstName>'+fn.Value +'</FirstName>' +
'<LastName>'+ln.Value +'</LastName>' +
'<Age>'+age.Value +'</Age>' +
'<Gender>'+gender.Value +'</Gender>' +
'</FieldValues>
An additional note: It is recommended to not use Camel Case on your xml since xml is case sensitive any use of case is a pain -- most just use all lower case.

Resources