Applying a Function to One Row in a Column - sql-server

I'm trying to apply a splitting function I wrote to one specific row in a table. Later, I'd like to apply the function to the entire column but I'm having trouble with the first step.
I've basically tried every variation below.
SalesPersons is the column I want to apply the split function to.
SELECT ID,
(SELECT ITEM
FROM [dbo].[Split](SalesPersons, ','))
FROM [dbo].[Menu]
WHERE ID = '1234'
AND APPLICANT = 'JohnSmith'
SELECT * dbo.Split(SalesPersons, ',')
FROM [dbo].[Menu]
WHERE ID = '1234'
AND APPLICANT = 'JohnSmith'
I keep getting the following result:
Cannot find either column dbo or the user-defined function or aggregate dbo.Split, or the name is ambiguous.
If anyone could provide any feedback or help, I'd appreciate it so much!

I'm assuming your split function is a Table-Valued Function
Then you would need a CROSS APPLY (use OUTER APPLY to see null values)
Example
Select A.*
,B.*
From [dbo].[Menu] A
Cross Apply dbo.Split(SalesPersons, ',') B
Where ID = '1234'
and APPLICANT = 'JohnSmith'
EDIT - If you want the split values in one row
Select A.*
,B.*
From [dbo].[Menu] A
Cross Apply (
Select Pos1 = ltrim(rtrim(xDim.value('/x[1]','varchar(100)')))
,Pos2 = ltrim(rtrim(xDim.value('/x[2]','varchar(100)')))
,Pos3 = ltrim(rtrim(xDim.value('/x[3]','varchar(100)')))
,Pos4 = ltrim(rtrim(xDim.value('/x[4]','varchar(100)')))
From (Select Cast('<x>' + replace((Select replace(A.SalesPersons,',','§§Split§§') as [*] For XML Path('')),'§§Split§§','</x><x>')+'</x>' as xml) as xDim) as A
) B

Related

Converting spaces and brackets to unicode in query result

I am using an CROSS APPLY & and a custom-built XML function (instead UNPIVOT) in SQL Server 2012, to move values vertically within a table.
SELECT A.USERID
,B.[ITEM]
,B.VALUE
FROM #UPLOAD A
CROSS APPLY [DBO].[TVF-XML-UNPIVOT-ROW]( (SELECT A.* FOR XML RAW) ) B
WHERE [ITEM] NOT IN ('USERID' )
The query successfully unpivots everything, but in the process, converts spaces, dashes, brackets, etc into their respective UNICODE value, as follows:
EMPID ITEM VALUE
123 _x0027_October_x0020_Bonus_x0020__x0028_Perm_x0020__x002B__x0020_Temp_x0029_ 28.01
Expected output:
EMPID ITEM VALUE
123 October Bonus (Perm + Temp) 28.01
Below is the function, some temp values you can use, and the Code itself. Any how to format the string without the unicode values?
CREATE FUNCTION [dbo].[tvf-XML-UnPivot-Row](#XML xml)
Returns Table
As
Return (
Select Item = xAttr.value('local-name(.)', 'varchar(100)')
,Value = xAttr.value('.','varchar(max)')
From #XML.nodes('//#*') xNode(xAttr)
)
DROP TABLE #UPLOAD
CREATE TABLE #UPLOAD (USERID INT,['October Bonus (Perm + Temp)] FLOAT NOT NULL );
INSERT INTO #UPLOAD VALUES (123,20.3),(240,35)
SELECT A.USERID
,B.[ITEM]
,B.VALUE
FROM #UPLOAD A
CROSS APPLY [DBO].[TVF-XML-UNPIVOT-ROW]( (SELECT A.* FOR XML RAW) ) B
WHERE [ITEM] NOT IN ('USERID' )
EDIT: I recall now that you were NOT 2016+
If 2016+ You can use this function instead of the XML approach
CREATE FUNCTION [dbo].[tvf-JSON-Unpivot-Row](#json varchar(max))
Returns Table
As Return
Select [Key]
,Value
From OpenJson(#json)
Examples
Select A.ID
,B.*
from YourTable A
Cross Apply [dbo].[tvf-JSON-Unpivot-Row]( (Select A.* For JSON Path,Without_Array_Wrapper ) ) B
Where [Key] not in ('ID')
or to include NULLs
Select A.ID
,B.*
from YourTable A
Cross Apply [dbo].[tvf-JSON-Unpivot-Row]( (Select A.* For JSON Path,Without_Array_Wrapper,INCLUDE_NULL_VALUES ) ) B
Where [Key] not in ('ID')

How to update a hierarchyid column

I have a table called items which has a parent/child relation that I am converting to a hierarchyid.
I have followed this tutorial to do so.
All the steps of the tutorial works, except the final update statement.
I get the error message:
Implicit conversion from data type hierarchyid to nvarchar(max) is not allowed. Use the CONVERT function to run this query.
But this makes no sense. The field I am updating is a hierarchyid, not an nvarchar(nax). So I don't see where an nvarchar(max) field is involved.
drop table #children
CREATE TABLE #Children
(
ID int,
TenantId int,
ParentID int,
Num int
);
GO
CREATE CLUSTERED INDEX tmpind ON #Children(TenantId, ParentID, ID);
GO
INSERT #Children (ID, TenantId,ParentID, Num)
SELECT ID, TenantId, ParentID,
ROW_NUMBER() OVER (PARTITION BY TenantId, ParentID ORDER BY ParentId)
FROM Items
GO
SELECT * FROM #Children ORDER BY TenantId, ParentID, Num
GO
WITH paths(path, ID, ParentId, TenantId)
AS (
-- This section provides the value for the root of the hierarchy
SELECT hierarchyid::GetRoot() AS OrgNode, ID, ParentId, TenantId
FROM #Children AS C
WHERE ParentId IS NULL
UNION ALL
-- This section provides values for all nodes except the root
SELECT
CAST(p.path.ToString() + CAST(C.Num AS varchar(30)) + '/' AS hierarchyid),
C.ID , C.ParentId, C.TenantId
FROM #Children AS C
JOIN paths AS p
ON C.ParentID = P.ID
)
-- This select statement runs just fine and shows expected data.
--Select i.Id as ItemId, p.path, p.path.ToString() as LogicalNode, p.Id, p.ParentId, p.TenantId from Paths P
--join Items I on p.Id = i.Id
--order by P.TenantId, P.path
--Note that I have tried using the convert function, but it still fails with the same error message.
UPDATE I Set OrgNode = Convert(hierarchyid, P.path)
FROM Items I
JOIN Paths AS P
ON I.ID = P.ID
GO
EDIT
Strangely, this DBFiddle works.
It looks like column OrgNode is not of type hierachyid. You could use ToString()
UPDATE I Set OrgNode = P.path.ToString()
FROM Items I
JOIN Paths AS P
ON I.ID = P.ID
or alter table Items and change column type.
It looks like you solved your problem, but I would suggest saving the conversion to hierarchyid to the end. Like this:
WITH paths(path, ID, ParentId, TenantId)
AS (
-- This section provides the value for the root of the hierarchy
SELECT cast('/' as varchar(max)) AS OrgNode, ID, ParentId, TenantId
FROM #Children AS C
WHERE ParentId IS NULL
UNION ALL
-- This section provides values for all nodes except the root
SELECT
CAST(concat(p.path.ToString(), C.Num, '/') AS varchar(max)),
C.ID , C.ParentId, C.TenantId
FROM #Children AS C
JOIN paths AS p
ON C.ParentID = P.ID
)
-- This select statement runs just fine and shows expected data.
--Select i.Id as ItemId, p.path, p.path.ToString() as LogicalNode, p.Id, p.ParentId, p.TenantId from Paths P
--join Items I on p.Id = i.Id
--order by P.TenantId, P.path
--Note that I have tried using the convert function, but it still fails with the same error message.
UPDATE I Set OrgNode = Convert(hierarchyid, P.path)
FROM Items I
JOIN Paths AS P
ON I.ID = P.ID
GO
Note, I also changed the + style of concatenation for the concat() function so you don't have to mess around with converting C.Num to a varchar.

SQL IN clause multiple columns and multiple value

This query is fine works.
SELECT * FROM TABLE WHERE 330110042 IN (iItem01,iItem02,iItem03,iItem04,iItem05,iItem_1,iItem_2,iItem_3,iItem_4,iItem_5,iItem_6,iItem_7,iItem_8,iItem_9,iItem_10,iItem_11,iItem_12,iItem_13,iItem_14,iItem_15,iItem_16,iItem_17,iItem_18,iItem_19,iItem_20,iItem_21,iItem_22,iItem_23,iItem_24,iItem_25,iItem_26,iItem_27,iItem_28,iItem_29,iItem_30)
But this query didnt work.
SELECT * FROM TABLE WHERE 330110042, 330110002, 330110002 IN (iItem01,iItem02,iItem03,iItem04,iItem05,iItem_1,iItem_2,iItem_3,iItem_4,iItem_5,iItem_6,iItem_7,iItem_8,iItem_9,iItem_10,iItem_11,iItem_12,iItem_13,iItem_14,iItem_15,iItem_16,iItem_17,iItem_18,iItem_19,iItem_20,iItem_21,iItem_22,iItem_23,iItem_24,iItem_25,iItem_26,iItem_27,iItem_28,iItem_29,iItem_30)
How i work in SQL Server?
It's difficult to tell your exact goal here, but one possibility would be to turn the list of values into a table structure of its own. A Common Table Expression might work:
;WITH Ids AS
(
SELECT 330110042 AS Id
UNION ALL
SELECT 330110002
)
SELECT t.*
FROM [Table] t
INNER JOIN Ids i ON t.iItem01 = i.Id OR t.iItem02 = i.Id OR...
But, maybe a solution with UNPIVOT would be more elegant. I presume that your table has a primary key column called Id:
;WITH Unpivoted AS
(
SELECT Id, ColName, ColValue
FROM (SELECT Id, iItem01, iItem02, iItem03
FROM [Table] t) p
UNPIVOT
(ColValue FOR ColName IN (iItem01, iItem02, iItem03)) AS unpvt
)
SELECT t.*
FROM [Table] t
WHERE EXISTS (SELECT 1 FROM Unpivoted u
WHERE t.Id = u.Id
AND u.ColValue IN (330110042, 330110002))
Of course, you would add all the necessary columns. I added only the first three for this example.

Selecting from XML field where XML field = X and another XML field = Y

So to build off of this question, Selecting from XML field where XML field = X, and using the same SQL Fiddle, http://sqlfiddle.com/#!3/7c0a0/5.
I can't seem to figure out how to grab the record that has both an item that has a FIELD with the value of 'Payment method' and has an item that has the NEWVALUE of 25.
I tried the following and it didn't return any results. I am assuming because it's looking for everything in the where clause in one of the nodes which doesn't exist.
SELECT
ID
FROM
T1
CROSS APPLY
XmlField.nodes('/ITEMS/ITEM') as XTbl(XItem)
WHERE
XItem.exist('FIELD[.="Payment method"]') = 1
and XItem.exist('NEWVALUE[.="25"]') = 1
What am I missing?
If you want only single items where your condition holds then you can do:
select
T1.ID, T.C.query('.')
from T1
cross apply XMLFIELD.nodes('/ITEMS/ITEM[FIELD[.="Payment method"] and NEWVALUE[.="Debit"]]') as T(C)
or
select
T1.ID, T.C.query('.')
from T1
cross apply XMLFIELD.nodes('/ITEMS/ITEM') as T(C)
where
T.C.value('FIELD[1]', 'nvarchar(max)') = 'Payment method' and
T.C.value('NEWVALUE[1]', 'nvarchar(max)') = 'Debit'
sql fiddle demo
After you comment, I think, may be you need something like this:
select
T1.*
from T1
where
XMLFIELD.exist
('/ITEMS[
ITEM[FIELD[.="Payment method"] and NEWVALUE[.="Debit"]] and
ITEM[DATATYPE[.="4"] and NEWVALUE[.="25"]]
]'
) = 1
sql fiddle demo
;with cte as
(
SELECT
ID, t1.XMLFIELD, row_number() over (partition by ID order by ID) rn
FROM
t1
CROSS APPLY
XmlField.nodes('/ITEMS/ITEM') as XTbl(XItem)
)
select XMLFIELD from cte
where ID IN (
select ID from cte
where XMLFIELD.exist('/ITEMS/ITEM/FIELD[.="Payment method"]') = 1
and XMLFIELD.exist('/ITEMS/ITEM/NEWVALUE[.="25"]') = 1)
and rn=1
Or simpler...
select
*
from
t1
where
XMLFIELD.value('count(/ITEMS/ITEM/FIELD[.="Payment method"])','int') > 0
and
XMLFIELD.value('count(/ITEMS/ITEM/NEWVALUE[.="25"])','int') > 0

Combine multiple results in a subquery into a single comma-separated value

I've got two tables:
TableA
------
ID,
Name
TableB
------
ID,
SomeColumn,
TableA_ID (FK for TableA)
The relationship is one row of TableA - many of TableB.
Now, I want to see a result like this:
ID Name SomeColumn
1. ABC X, Y, Z (these are three different rows)
2. MNO R, S
This won't work (multiple results in a subquery):
SELECT ID,
Name,
(SELECT SomeColumn FROM TableB WHERE F_ID=TableA.ID)
FROM TableA
This is a trivial problem if I do the processing on the client side. But this will mean I will have to run X queries on every page, where X is the number of results of TableA.
Note that I can't simply do a GROUP BY or something similar, as it will return multiple results for rows of TableA.
I'm not sure if a UDF, utilizing COALESCE or something similar might work?
Even this will serve the purpose
Sample data
declare #t table(id int, name varchar(20),somecolumn varchar(MAX))
insert into #t
select 1,'ABC','X' union all
select 1,'ABC','Y' union all
select 1,'ABC','Z' union all
select 2,'MNO','R' union all
select 2,'MNO','S'
Query:
SELECT ID,Name,
STUFF((SELECT ',' + CAST(T2.SomeColumn AS VARCHAR(MAX))
FROM #T T2 WHERE T1.id = T2.id AND T1.name = T2.name
FOR XML PATH('')),1,1,'') SOMECOLUMN
FROM #T T1
GROUP BY id,Name
Output:
ID Name SomeColumn
1 ABC X,Y,Z
2 MNO R,S
1. Create the UDF:
CREATE FUNCTION CombineValues
(
#FK_ID INT -- The foreign key from TableA which is used
-- to fetch corresponding records
)
RETURNS VARCHAR(8000)
AS
BEGIN
DECLARE #SomeColumnList VARCHAR(8000);
SELECT #SomeColumnList =
COALESCE(#SomeColumnList + ', ', '') + CAST(SomeColumn AS varchar(20))
FROM TableB C
WHERE C.FK_ID = #FK_ID;
RETURN
(
SELECT #SomeColumnList
)
END
2. Use in subquery:
SELECT ID, Name, dbo.CombineValues(FK_ID) FROM TableA
3. If you are using stored procedure you can do like this:
CREATE PROCEDURE GetCombinedValues
#FK_ID int
As
BEGIN
DECLARE #SomeColumnList VARCHAR(800)
SELECT #SomeColumnList =
COALESCE(#SomeColumnList + ', ', '') + CAST(SomeColumn AS varchar(20))
FROM TableB
WHERE FK_ID = #FK_ID
Select *, #SomeColumnList as SelectedIds
FROM
TableA
WHERE
FK_ID = #FK_ID
END
In MySQL there is a group_concat function that will return what you're asking for.
SELECT TableA.ID, TableA.Name, group_concat(TableB.SomeColumn)
as SomColumnGroup FROM TableA LEFT JOIN TableB ON
TableB.TableA_ID = TableA.ID
I think you are on the right track with COALESCE. See here for an example of building a comma-delimited string:
http://www.sqlteam.com/article/using-coalesce-to-build-comma-delimited-string
You may need to provide some more details for a more precise response.
Since your dataset seems kind of narrow, you might consider just using a row per result and performing the post-processing at the client.
So if you are really looking to make the server do the work return a result set like
ID Name SomeColumn
1 ABC X
1 ABC Y
1 ABC Z
2 MNO R
2 MNO S
which of course is a simple INNER JOIN on ID
Once you have the resultset back at the client, maintain a variable called CurrentName and use that as a trigger when to stop collecting SomeColumn into the useful thing you want it to do.
Assuming you only have WHERE clauses on table A create a stored procedure thus:
SELECT Id, Name From tableA WHERE ...
SELECT tableA.Id AS ParentId, Somecolumn
FROM tableA INNER JOIN tableB on TableA.Id = TableB.F_Id
WHERE ...
Then fill a DataSet ds with it. Then
ds.Relations.Add("foo", ds.Tables[0].Columns("Id"), ds.Tables[1].Columns("ParentId"));
Finally you can add a repeater in the page that puts the commas for every line
<asp:DataList ID="Subcategories" DataKeyField="ParentCatId"
DataSource='<%# Container.DataItem.CreateChildView("foo") %>' RepeatColumns="1"
RepeatDirection="Horizontal" ItemStyle-HorizontalAlign="left" ItemStyle-VerticalAlign="top"
runat="server" >
In this way you will do it client side but with only one query, passing minimal data between database and frontend
I tried the solution priyanka.sarkar mentioned and the didn't quite get it working as the OP asked. Here's the solution I ended up with:
SELECT ID,
SUBSTRING((
SELECT ',' + T2.SomeColumn
FROM #T T2
WHERE WHERE T1.id = T2.id
FOR XML PATH('')), 2, 1000000)
FROM #T T1
GROUP BY ID
Solution below:
SELECT GROUP_CONCAT(field_attr_best_weekday_value)as RAVI
FROM content_field_attr_best_weekday LEFT JOIN content_type_attraction
on content_field_attr_best_weekday.nid = content_type_attraction.nid
GROUP BY content_field_attr_best_weekday.nid
Use this, you also can change the Joins
SELECT t.ID,
t.NAME,
(SELECT t1.SOMECOLUMN
FROM TABLEB t1
WHERE t1.F_ID = T.TABLEA.ID)
FROM TABLEA t;
This will work for selecting from different table using sub query.
I have reviewed all the answers. I think in database insertion should be like:
ID Name SomeColumn
1. ABC ,X,Y Z (these are three different rows)
2. MNO ,R,S
The comma should be at previous end and do searching by like %,X,%

Resources