Using Table-Valued Functions in SQL Server - sql-server

I'm sorry for I'm not very good at English.
My T-SQL function:
create function sumofOrder(#thang int, #nam int)
returns table
as
return
SELECT
Sales.SalesOrderDetail.SalesOrderID,
Sales.SalesOrderHeader.OrderDate,
SUM(Sales.SalesOrderDetail.OrderQty * Sales.SalesOrderDetail.UnitPrice) AS SubTotal
FROM
Sales.SalesOrderDetail
INNER JOIN
Sales.SalesOrderHeader ON Sales.SalesOrderDetail.SalesOrderID = Sales.SalesOrderHeader.SalesOrderID
GROUP BY
Sales.SalesOrderDetail.SalesOrderID,
Sales.SalesOrderHeader.OrderDate
HAVING
(SUM(Sales.SalesOrderDetail.OrderQty * Sales.SalesOrderDetail.UnitPrice) > 70000)
AND YEAR(Sales.SalesOrderHeader.OrderDate) = #nam
AND MONTH(Sales.SalesOrderHeader.OrderDate) = #thang
And now, I want to use it for filtering by #thang and #nam and not any specific values.
Example of a scalar function:
select
[DepartmentID], [Name], dbo.cau1([DepartmentID]) as 'tongnhanvien'
from
[HumanResources].[Department]
Thank you everyone!
ps: my idea is: select * from [dbo].[sumofOrder](MONTH(Sales.SalesOrderHeader.OrderDate), YEAR(Sales.SalesOrderHeader.OrderDate))

Table-Valued User-Defined Functions
simply select from it:
select * from sumofOrder(thangValue, namValue)
Also Using APPLY
The APPLY operator allows you to invoke a table-valued function for
each row returned by an outer table expression of a query. The
table-valued function acts as the right input and the outer table
expression acts as the left input. The right input is evaluated for
each row from the left input and the rows produced are combined for
the final output. The list of columns produced by the APPLY operator
is the set of columns in the left input followed by the list of
columns returned by the right input.
EDIT
SELECT f.*
FROM Sales.SalesOrderHeader s
CROSS APPLY [dbo].[sumofOrder](month(s.OrderDate), year(s.OrderDate)) f

Related

Combine NVARCHAR column with the result of a SUM in SQL Server

How can I combine a NVARCHAR column with the result of a SUM in SQL Server?
I have tried
dbo.tblCurrencies.strCurrencySymbol + dbo.tblItems.dcUnitPrice * dbo.tblItems.intItemQuantity
Which tells me:
Error converting data type nvarchar to numeric
So I tried
dbo.tblCurrencies.strCurrencySymbol + CAST(SUM(dbo.tblItems.dcUnitPrice * dbo.tblItems.intItemQuantity) AS NVARCHAR)
AND
CAST(dbo.tblCurrencies.strCurrencySymbol + SUM(dbo.tblItems.dcUnitPrice * dbo.tblSalesOrderItems.intItemQuantity) AS NVARCHAR)
Both tell me
Cannot use an aggregate or a subquery in an expression used for the group by list of a group by clause.
Any more information you need just let me know I'll put it up.
EDIT:
The query
SELECT dbo.tblItems.fkOrder, dbo.tblCurrencies.strCurrencySymbol + SUM(dbo.tblItems.dcUnitPrice * dbo.tblItems.intItemQuantity) AS TotalPrice
FROM dbo.tblCurrencies RIGHT OUTER JOIN
dbo.tblOrders ON dbo.tblCurrencies.pkCurrency = dbo.tblOrders.fkPaysInCurrency RIGHT OUTER JOIN
dbo.tblItems ON dbo.tblOrders.pkOrder = dbo.tblItems.fkOrder
GROUP BY dbo.tblItems.fkOrder
EDIT 2:
Ok I managed to solve it by adding dbo.tblCurrencies.strCurrencySymbol into the group by and using CONCAT()
My query now looks like this:
SELECT dbo.tblItems.fkOrder, { fn CONCAT(dbo.tblCurrencies.strCurrencySymbol, CAST(SUM(dbo.tblItems.dcUnitPrice * dbo.tblItems.intItemQuantity) AS NVARCHAR(10))) } AS TotalPrice
FROM dbo.tblCurrencies RIGHT OUTER JOIN
dbo.tblOrders ON dbo.tblCurrencies.pkCurrency = dbo.tblOrders.fkPaysInCurrency RIGHT OUTER JOIN
dbo.tblItems ON dbo.tblOrders.pkOrder = dbo.tblItems.fkOrder
GROUP BY dbo.tblItems.fkOrder, dbo.tblCurrencies.strCurrencySymbol
It also works without the CONCAT()
SELECT dbo.tblItems.fkOrder, dbo.tblCurrencies.strCurrencySymbol + CAST(SUM(dbo.tblItems.dcUnitPrice * dbo.tblItems.intItemQuantity) AS NVARCHAR(10)) AS TotalPrice
FROM dbo.tblCurrencies RIGHT OUTER JOIN
dbo.tblOrders ON dbo.tblCurrencies.pkCurrency = dbo.tblOrders.fkPaysInCurrency RIGHT OUTER JOIN
dbo.tblItems ON dbo.tblOrders.pkOrder = dbo.tblItems.fkOrder
GROUP BY dbo.tblItems.fkOrder, dbo.tblCurrencies.strCurrencySymbol
Not sure which is better though?
In SQL Server there's a concept named Data Type Precedence. It's there in cases such as these when two different data types need to be combined because there needs to be a method to decide on what the data type of the final output will be.
In your case you're combining NVARCHAR and NUMERIC data types and as NUMERIC has the higher precedence the approach it takes is to try and convert your text data into a number which it can't do, hence the error.
If you need the conversion to run in a way other than defined in the normal precedence order then you need to explicitly make the conversion yourself with either CAST or CONVERT. In your case you need to multiply the needed numbers, convert those to text and then concatenate the currency symbol.
Subsequent errors come from using fields that are neither within an aggregate or specified in the Group By of your select statement. Adding you currency field in there should resolve the other issue assuming you can't have one order with multiple currencies involved.
Putting it all together gives you the following:
SELECT
Orders.fkOrder,
Currencies.strCurrencySymbol
+ CAST(SUM(Items.dcUnitPrice * Items.intItemQuantity) AS varchar(50)) AS TotalPrice
FROM dbo.tblItems AS Items
LEFT OUTER JOIN dbo.tblOrders AS Orders
ON Items.fkOrder = Orders.pkOrder
LEFT OUTER JOIN dbo.tblCurrencies AS Currencies
ON Orders.fkPaysInCurrency = Currencies.pkCurrency
GROUP BY Orders.fkOrder, Currencies.strCurrencySymbol
Based on the query fragment that you have posted, I would try something along the lines of
dbo.tblCurrencies.strCurrencySymbol +
CAST(SUM(dbo.tblItems.dcUnitPrice * dbo.tblSalesOrderItems.intItemQuantity) AS NVARCHAR(x))
Points to note: the cast to NVARCHAR is done on the sum part only
x is the length of the NVARCHAR string that you wish to cast it to (make sure that it is wide enough to hold the largest value that the sum can return).
Using the schema name prior to the column name is deprecated.

I need a case to get value from another table when join returns null value

First just take a look from my image
The tables:
I need when join doesnt find anything to get the value from my second table
How can i do that?
Here is my code:
select * from GridHour
select * from Grid
SELECT a.HistoryID1,a.CustomerID1,CONVERT(VARCHAR(5), b.Hour,108),a.Text1,a.Text2,a.HistoryID2,a.CustomerID2 from Grid a full join GridHour b on a.AppointmentHour=b.hour
You need to put the 2 joins and use the isNull function like this:
*If the first value is null, take the other value like in the example
select * from GridHour
select * from Grid
SELECT
a.HistoryID1,
a.CustomerID1,
CONVERT(VARCHAR(5),isnull(b.Hour,c.Hour),108), -- Here put the isnull function
a.Text1,
a.Text2,
a.HistoryID2,
a.CustomerID2
from Grid a
full join GridHour b
on a.AppointmentHour=b.hour
left join Grid c
on a.HistoryID=b.GridID -- Not sure if this is the link

Select IDs which belongs ONLY to the list passed as parameter

Let's start from data:
DECLARE #Avengers TABLE ([Hero] varchar(32), [Preference] varchar(32));
INSERT INTO #Avengers VALUES
('Captain_America','gingers'),('Captain_America','blondes'),
('Captain_America','brunettes'),('Hulk','gingers'),('Hulk','blondes'),
('Hawkeye','gingers'),('Hawkeye','brunettes'),('Iron_Man','blondes'),
('Iron_Man','brunettes'),('Thor','gingers'),('Nick_Fury','blondes');
Now I would like to pass a #Preferences as a list of [Preference] (either comma separated or single column table parameter) without knowing how many parameters I am going to get and based on this to select [Hero] who prefers exactly these #Preferences as provided in parameter (list), by that I mean if I am after 'blondes' and 'gingers' then I am after 'Hulk' only
(NOT 'Captain_America' who prefers 'blondes', 'gingers' and 'brunettes').
I would like to get something like:
SELECT [Hero]
FROM #Avengers
WHERE *IS_ASSIGNED_ONLY_TO_THE_LIST*([Preference]) = #Preference
Well, I think I overcomplicated my code, but it works.
SELECT a.Hero, COUNT(*), MIN(p.N)
FROM #Avengers a
LEFT JOIN ( SELECT *, COUNT(*) OVER() N
FROM #Preferences) p
ON a.Preference = p.Preference
GROUP BY a.Hero
HAVING COUNT(*) = MIN(p.N)
AND COUNT(*) = COUNT(p.Preference)
;
I'm using #Preferences as a table.

Cross Apply yields nulls

I'm quoting MS:
CROSS APPLY returns only rows from the outer table that produce a
result set from the table-valued function.
This would mean that it would not return rows with null values, right?
However, my query is:
select ....,cat_custom,....
from ...(various inner joins)...
cross apply
(
select
case
when i.cat1='01' then 1
when i.cat2='04' then 2
when i.cat2='07' then 3
when i.cat2 in ('08') or i.cat3 in ('014','847') then 4
else null
end as cat_custom
) as cat_custom_query
...and sure enough, I get rows with nulls. Wouldn't that be OUTER apply's job? What is going on?
CROSS APPLY returns only rows from the outer table that produce a
result set from the table-valued function.
In your example, a row is produced - row, which is returning a NULL value.
You can try this:
select ....,cat_custom,....
from ...(various inner joins)...
cross apply
(
select
case
when i.cat1='01' then 1
when i.cat2='04' then 2
when i.cat2='07' then 3
when i.cat2 in ('08') or i.cat3 in ('014','847') then 4
else null
end as cat_custom
WHERE i.cat1 IN ('01', '04', '07', '08', '014', '847')
) as cat_custom_query
Also, if this is part of your real query, you can add the result column in the SELECT statement. You do not need to use CROSS APPLY here, as you are not referring any SQL objects (tables, views, functions ,etc).

Apply SQL Function to a Table Column Values

I have a Transact SQL function SimpleSplit, which splits a string according to the delimiter. I can use it as follows:
DECLARE #DelimitedString NVARCHAR(128)
SET #DelimitedString = 'Processor,RAM,SSD,Ethernet'
SELECT * FROM [dbo].[SimpleSplit](#DelimitedString, ',')
This results in:
Processor
RAM
SSD
Ethernet
As expected.
I now have a Table called PROD_TABLE with a column Descr. I would like to apply this function to each value in column Descr. I attempted the following and it does not work:
SELECT p.[ID], p.[Descr]
FROM [Amazon].[dbo].[PROD_TABLE] p
OUTER APPLY [dbo].[SimpleSplit](p.Descr, '-') d
In the output I only see the ID and the Descr Columns i.e. no results from the SimpleSplit function. However, if I attempt
SELECT *
FROM [Amazon].[dbo].[PROD_TABLE] p
OUTER APPLY [dbo].[SimpleSplit](p.Descr, '-') d
I see the results of the SimpleSplit function in the last column. Why does this query apply the function, but the previous query does not?
Answer
Thanks to mr.Rebands answer below, I realized that I needed to name the results. Hence * worked, but to explicitly name the columns I needed to do something like:
SELECT p.[ID], p.[Descr], d.[Data]
FROM [Amazon].[dbo].[PROD_TABLE] p
OUTER APPLY [dbo].[SimpleSplit](p.[Descr], '-') d
Your function returns a table - what is the column name of the SimpleSplit result table? You will have to include that column name in your select statement.
OUTER APPLY is applied but the results are not selected.

Resources