How to create function in SQL Server for the following scenario - tsql

I need to create a function which returns a string. Input to function is studentid:
select *
from StudentReference
where studentid = ?
order by Subid
Table format is like this:
studentid subname subid
------------------------------
1 math 2
1 sci,xx 3
1 social 1
Function input is studentid, it should order by subid as above and function should return string social~math~sci,xx
Can anybody give an idea ?
select
T1.studentid,
stuff((select '~' + T2.subname
from Studenttable as T2
where T1.studentid = T2.studentid
for xml path(''), type).value('.', 'varchar(max)'), 1, 1, '') as subname
from
Studenttable as T1
group by
T1.studentid
How to convert above logic as studentid as i/p for table order by subid in function?

Related

How to display Text Unit only one time if it repeated for same Feature when do stuff?

I work with SQL Server 2012 and face an issue: I can't display Text Unit only one time where it repeated for feature using Stuff.
What I need is when Text Unit is repeated for same feature, then no need to repeat it - only display it once.
In my case, I face issue that I can't prevent repeat Text Unit when It be same Text Unit for same Feature.
Voltage | Voltage | Voltage ONLY one Voltage display .
CREATE TABLE #FinalTable
(
PartID INT,
DKFeatureName NVARCHAR(100),
TextUnit NVARCHAR(100),
StatusId INT
)
INSERT INTO #FinalTable (PartID, DKFeatureName, TextUnit, StatusId)
VALUES
(1211, 'PowerSupply', 'Voltage', 3),
(1211, 'PowerSupply', 'Voltage', 3),
(1211, 'PowerSupply', 'Voltage', 3)
SELECT
PartID, DKFeatureName,
COUNT(PartID) AS CountParts,
TextUnit = STUFF ((SELECT ' | ' + TextUnit
FROM #FinalTable b
WHERE b.PartID = a.PartID
AND a.DKFeatureName = b.DKFeatureName
AND StatusId = 3
FOR XML PATH('')), 1, 2, ' ')
INTO
#getUnitsSticky
FROM
#FinalTable a
GROUP BY
PartID, DKFeatureName
HAVING
(COUNT(PartID) > 1)
SELECT *
FROM #getUnitsSticky
Expected result is :
Voltage
Incorrect result or result I don't need is as below :
Voltage|Voltage|Voltage
TomC's answer is basically correct. However, when using this method with SQL Server, it is usually more efficient to get the rows in a subquery and then use stuff() in the outer query. That way, the values in each row are processed only once.
So:
SELECT PartID, DKFeatureName, CountParts,
STUFF( (SELECT ' | ' + TextUnit
FROM #FinalTable b
WHERE b.PartID = a.PartID AND
b.DKFeatureName = a.DKFeatureName AND
StatusId = 3
FOR XML PATH('')
), 1, 3, ' ') as TextUnit
INTO #getUnitsSticky
FROM (SELECT PartID, DKFeatureName, COUNT(*) as CountParts
FROM #FinalTable a
GROUP BY PartID, DKFeatureName
HAVING COUNT(*) > 1
) a;
This also removes the leading space from the concatenated result.
To put this into a complete answer - this should be your SQL (shortened slightly and removed the last temp table):
SELECT
PartID, DKFeatureName,
COUNT(PartID) AS CountParts,
TextUnit = STUFF ((SELECT distinct ' | ' + TextUnit
FROM #FinalTable b
WHERE b.PartID = a.PartID
AND a.DKFeatureName = b.DKFeatureName
AND StatusId = 3
FOR XML PATH('')), 1, 2, ' ')
FROM #FinalTable a
GROUP BY PartID, DKFeatureName
HAVING (COUNT(PartID) > 1)

If there is only one zero value then group by supplier and show zero, if there is no zero, then avg all values

I will give you example of table that I have:
Supplier | Value
sup1 | 4
sup2 | 1
sup1 | 0
sup1 | 3
sup2 | 5
I need a result that will do average by supplier, but if there is value 0 for a supplier, do not average, but return 0 instead
It should look like this:
Supplier | Value
sup1 | 0
sup2 | 3
This is a little trick but it should work :
SELECT Supplier,
CASE WHEN MIN(ABS(Value)) = 0 THEN 0 ELSE AVG(Value) END
FROM TableTest
GROUP BY Supplier
EDIT : Using the ABS() function let you avoid having problems with negative values
DECLARE #TAB TABLE (SUPPLIER VARCHAR(50),VALUE INTEGER)
INSERT INTO #TAB
SELECT 'sup1',4
UNION ALL
SELECT 'sup2',1
UNION ALL
SELECT 'sup1',0
UNION ALL
SELECT 'sup1',3
UNION ALL
SELECT 'sup2',5
SELECT * FROM #TAB
SELECT T1.SUPPLIER,CASE WHEN EXISTS(SELECT 1 FROM #TAB T WHERE T.SUPPLIER = T1.SUPPLIER AND T.VALUE = 0) THEN 0 ELSE AVG(T1.VALUE) END AS VALUE
FROM #TAB T1
GROUP BY T1.SUPPLIER
Result
SUPPLIER VALUE
sup1 0
sup2 3
Using the following query is one of the way to do.
First I push the supplier which has the Value = 0, then based on the result, I will do the remaining calculation and finally using UNION to get the expected result:
DECLARE #ZeroValue TABLE (Supplier VARCHAR (20));
INSERT INTO #ZeroValue (Supplier)
SELECT Supplier FROM TestTable WHERE Value = 0
SELECT Supplier, 0 AS Value FROM #ZeroValue
UNION
SELECT T.Supplier, AVG(T.Value) AS Value
FROM TestTable T
JOIN #ZeroValue Z ON Z.Supplier != T.Supplier
GROUP BY T.Supplier
Schema used for the sample:
CREATE TABLE TestTable (Supplier VARCHAR (20), Value INT);
INSERT INTO TestTable (Supplier, Value) VALUES
('sup1', 4), ('sup2', 1), ('sup1', 0), ('sup1', 3), ('sup2', 5);
Please find the working demo on db<>fiddle

Parsing a comma delimited column and transposing into rows [duplicate]

This question already has answers here:
Turning a Comma Separated string into individual rows
(16 answers)
Closed 5 years ago.
Suppose I have a table like this with an undetermined number of comma-delimited values in one column:
thingID personID
1 123,234,345
2 456,567
and I want to get it into a form like this:
thingID personID
1 123
1 234
1 345
2 456
2 567
What is my best option for doing this?
Oh I should mention the data is in a SQL 2008 R2 database so I may not be able to use the very latest functionality.
Use CROSS APPLY with a string splitting function.
To find the string splitting function that works best for you, read Aaron Bertrand's Split strings the right way – or the next best way.
For this demonstration I've chosen to use the SplitStrings_XML function, simply because it's the first pure t-sql function in the article:
CREATE FUNCTION dbo.SplitStrings_XML
(
#List NVARCHAR(MAX),
#Delimiter NVARCHAR(255)
)
RETURNS TABLE
WITH SCHEMABINDING
AS
RETURN
(
SELECT Item = y.i.value('(./text())[1]', 'nvarchar(4000)')
FROM
(
SELECT x = CONVERT(XML, '<i>'
+ REPLACE(#List, #Delimiter, '</i><i>')
+ '</i>').query('.')
) AS a CROSS APPLY x.nodes('i') AS y(i)
);
GO
Now that we have a string splitting function, create and populate the sample table (Please save us this step in your future questions):
DECLARE #T AS TABLE
(
thingID int,
personID varchar(max)
)
INSERT INTO #T VALUES
(1, '123,234,345'),
(2, '456,567')
The query:
SELECT thingId, Item
FROM #T
CROSS APPLY dbo.SplitStrings_XML(personID, ',')
Results:
thingId Item
1 123
1 234
1 345
2 456
2 567
You can see a live demo on rextester.
There are several ways to do that. Here are two methods for SQL Server 2008:
XML-Method: requires the string to allow for the xml-trick (no invalid XML chars)
SELECT a.thingID, Split.a.value('.', 'VARCHAR(100)') AS Data
FROM (SELECT OtherID,
CAST('<M>' + REPLACE(personID, ',', '</M><M>') + '</M>' AS XML) AS Data
FROM table1) AS A CROSS APPLY Data.nodes ('/M') AS Split(a);
Recursive method:
;WITH tmp(thingID, DataItem, Data) AS (
SELECT thingID, LEFT(personID, CHARINDEX(',', personID + ',') - 1),
STUFF(personID, 1, CHARINDEX(',', personID + ','), '')
FROM table1
UNION ALL
SELECT thingID, LEFT(personID, CHARINDEX(',', personID + ',') - 1),
STUFF(personID, 1, CHARINDEX(',', personID + ','), '')
FROM tmp
WHERE Data > ''
)
SELECT thingID, DataItem AS personID
FROM tmp

Concatenate results from another table

I have two tables
TableA has three fields
Id | FieldA | SomethingElse
TableB has three fields as well
Id | FieldA_FK | FieldB
We can join the tables on
TableA.FieldA = TableB.FieldA_FK
I would like to select values on both these tables in order to retrieve the following dataset:
TableA.Id, TableA.FieldA, TableA.SomethingElse, [Concatenation of TableB.FieldB]
To retrieve [Concatenation of TableB.FieldB], I know I can do
declare #result varchar(500);
set #result = '';
select #result = COALESCE(#result + ',', '') + FieldB
from TableB b
join TableA a on a.FieldA = b.FieldA_FK
select #result
How can I get the result described above with the concatenation on one result row only?
Thanks in advance.
Examples of data:
TableA
1 A something
2 B somethingElse
TableB
1 A Aa
2 A Ab
3 A Ac
4 B Ba
5 B Bb
I would like to retrieve
1 A something Aa, Ab, Ac
2 B somethingElse Ba, Bb
You can use the FOR XML PATH command:
SELECT
TableA.Id, TableA.FieldA, TableA.SomethingElse,
[Concatenation of TableB.FieldB] =
(STUFF((SELECT CAST(', ' + TableB.FieldB AS VARCHAR(MAX))
FROM TableB
WHERE (TableA.FieldA = TableB.FieldA_FK)
FOR XML PATH ('')), 1, 2, ''))
FROM TableA
Demo
This isn't as obvious as it should be, but you can abuse SQL's XML methods:
select a.ID, a.FieldA, a.FieldB
, stuff(
(select ', ' + TableB.FieldB
from TableB
inner join TableA on TableB.TableA_FK = TableA.ID
for xml path(''), type
).value('(./text())[1]', 'varchar(max)')
, 1,2,'')
from TableA a
See this fiddle:
http://sqlfiddle.com/#!3/0fdd52/5

Employee Salary Should display monthwise as moth displaying Horizontally as Headings

My requirement is as follows:
Am using Postgresql and ireport 4.0.1 for generating this report.
I've four tables like g_employee,g_year,g_period,g_salary, by joining these four tables and passing parameter are fromDate and toDate these parameter values like '01/02/14' between '01/05/14'.Based this parameters the displaying months will be vary in the headings as i shown in the below example:
EmpName
01/02/14 01/03/14 01/04/14 01/05/14
abc
2000 3000 3000 2000
Can anyone help me in this getting output?
What you're describing sounds like the number of columns would grow or shrink based on the number of months between the 2 parameters, which just doesn't work.
I don't know any way to add additional columns based on an interval between 2 parameters without a procedural code generated sql statement.
What is possible is:
emp_id1 period1 salary
emp_id1 period2 salary
emp_id1 period3 salary
epd_id1 period4 salary
emp_id2 period1 salary
emp_id2 period2 salary
emp_id2 period3 salary
epd_id2 period4 salary
generated with something like:
select g_employee_id,
g_period_start,
g_salary_amt
from g_employee, g_year, g_period, g_salary
where <join everything>
and g_period_start between date_param_1 and date_param_2
group by g_employee_id, g_period_start;
Hard to get more specific with out the table structure.
As the range between date_param_1 and date_param_2 grew, the number of rows would grow for each employee with pay in that "g_period"
EDIT - Other option:
The less dynamic option which requires more parameters would be:
select g_employee_id,
(select g_salary_amount
from g_period, g_salary
where g_period_id = g_salary_period_id
and g_salard_emp_id = g_employee_id
and g_period_start = <DATE_PARAM_1> ) as "DATE_PARAM_1_desc",
(select g_salary_amount
from g_period, g_salary
where g_period_id = g_salary_period_id
and g_salard_emp_id = g_employee_id
and g_period_start = <DATE_PARAM_2> ) as "DATE_PARAM_2_desc",
(select g_salary_amount
from g_period, g_salary
where g_period_id = g_salary_period_id
and g_salard_emp_id = g_employee_id
and g_period_start = <DATE_PARAM_3> ) as "DATE_PARAM_3_desc"
,..... -- dynamic not possible
from employee;
i create one table #g_employee and insert dummy data
create table #g_employee(empid int,yearid int,periodid int,salary int)
insert into #g_employee(empid,yearid,periodid,salary)
select 1,2014,02,2000
union
select 2,2014,02,2000
union
select 3,2014,02,2000
union
select 3,2014,03,2000
union
select 1,2014,03,3000
union
select 1,2014,04,4000
output query as per your requirement :
Solution 1 :
select empid, max(Case when periodid=2 and yearid=2014 then salary end) as '01/02/2014'
, max(Case when periodid=3 and yearid=2014 then salary end) as '01/03/2014'
, max(Case when periodid=4 and yearid=2014 then salary end) as '01/04/2014'
from #g_employee
group by empid
you can do with dynamic sql :
Solution 2 :
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
select #cols = STUFF((SELECT ',' + QUOTENAME(periodid)
from #g_employee
group by periodid
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = 'SELECT empid,' + #cols + ' from
(
select salary, periodid,empid
from #g_employee
) x
pivot
(
max(salary)
for periodid in (' + #cols + ')
) p '
execute(#query)
hope this will help