Combine NVARCHAR column with the result of a SUM in SQL Server - 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.

Related

XML path not producing concatenated results in sql joined table

I am trying to make a table that shows all the patients checked in to the hospital. I can join client, patient, check-in, appointment data just fine, but the alerts table has multiple rows which I am trying to aggregate/concatenate/rollup. I tried to create an XML statement but it doesn't seem to be working. I would like for all the alerts for the patient to be a single comma-separated string in one row. here is what I have:
select DISTINCT
a.ResourceAbbreviation1, a.AppointmentType, a.StatusNum, c.sLastName,
pt.Name, pt.WeightString, pt.AgeShort, pt.Breed, pt.Species, pt.Gender, pt.NewPatient,
(select SUBSTRING((
select ',' + al.stext AS 'data()'
FOR XML PATH('')
), 2, 9999) as cautions),
pt.Classification, p.kPatientId
from dbo.entpatients pt
join alerts al
on al.kpatientid = pt.IDPatient
join dbo.PatientCheckIns P
on pt.IDPatient=p.kPatientId
join dbo.EntAppointments a
on a.IDPatient = p.kPatientId
join dbo.clients c
on c.kID=a.IDClient
where cast (a.StartTime as date) = cast(getdate() as date)
and a.StatusNum=4;
You need to move your alerts table reference inside the subselect as a FROM.
I also suggest using AS text() instead of AS data() (or omitting it entirely) to avoid unwanted spaces, and using STUFF() instead of SUBSTRING() to strip the leading comma. The extra nested SELECT is also unneeded.
select DISTINCT
a.ResourceAbbreviation1, a.AppointmentType, a.StatusNum, c.sLastName,
pt.Name, pt.WeightString, pt.AgeShort, pt.Breed, pt.Species, pt.Gender, pt.NewPatient,
STUFF((
select ',' + al.stext AS 'text()'
from alerts al
where al.kpatientid = pt.IDPatient
FOR XML PATH('')
), 1, 1, '') as cautions,
pt.Classification,
p.kPatientId
from dbo.entpatients pt
join dbo.PatientCheckIns P
on pt.IDPatient=p.kPatientId
join dbo.EntAppointments a
on a.IDPatient = p.kPatientId
join dbo.clients c
on c.kID=a.IDClient
where cast (a.StartTime as date) = cast(getdate() as date)
and a.StatusNum=4;
If there is any chance that your alert text may contain special XML characters (such as <, >, or &) that might get encoded, I recommend a slightly modified form that uses the .value() function to extract the concatenated text.
STUFF((
select ',' + al.stext AS 'text()'
from alerts al
where al.kpatientid = pt.IDPatient
FOR XML PATH(''), TYPE
).value('text()[1]','nvarchar(max)'), 1, 1, '') as cautions,
This avoids seeing encodings like <, >, and & in the results. See this for more.
If you are using SQL server 2017 or later, you could also switch to the relatively new STRING_AGG() function. See here.
(
select STRING_AGG(',', al.stext)
from alerts al
where al.kpatientid = pt.IDPatient
) as cautions,
I would also review your need for the DISTINCT. In some cases, it is appropriate when you knowingly expect your query to return duplicate rows that you wish to eliminate. For example, if you know you may have multiple visits by the same patient with identical selected data, DISTICT may be appropriate. However, if you have dropped it in to eliminate duplicates without knowing why, it may be a sign of an under-constrained join or other logic problems that warrant a further look.

Error converting data type varchar to numeric on join clause

Please help with this query. I am getting an
error converting data type varchar to numeric" on my join clause.
select
MARKETING
,HOME_TEL
,BUS_TEL
,CEL_TEL
,EMAIL
,FAX
,VALID_MAIL
,VALID_PHONE
,VALID_SMS
,VALID_EMAIL
into [storagedb - baw].dbo.Geyser_Glynis
from [storagedb - Mariana].dbo.HOC_Geyser_v2 as a
left join [IIIDB].[dbo].[EEE_BASE_201901] as b
on a.gcustomer_Number= b.Dedupe_Static
Execute these 2 queries to find values that you are trying to join together that can't be cast to numeric:
SELECT
T.gcustomer_Number
FROM
[storagedb - Mariana].dbo.HOC_Geyser_v2 AS T
WHERE
TRY_CAST(T.gcustomer_Number AS NUMERIC) IS NULL
SELECT
T.Dedupe_Static
FROM
[IIIDB].[dbo].[EEE_BASE_201901] AS T
WHERE
TRY_CAST(T.Dedupe_Static AS NUMERIC) IS NULL
You will have to delete or update them to be able to be converted correctly to a number or, if one of the columns might hold non-numeric values, you will have to cast the numeric column to VARCHAR, for example:
from
[storagedb - Mariana].dbo.HOC_Geyser_v2 as a
left join [IIIDB].[dbo].[EEE_BASE_201901] as b on
CONVERT(VARCHAR(100), a.gcustomer_Number) = b.Dedupe_Static
Please note that applying conversion or functions against indexed columns will make the index inapplicable and most likely end in a full table scan.

Using Table-Valued Functions in 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

ORDER BY not putting SELECT statement in numerical order

I am working on a SELECT statement.
USE SCRUMAPI2
DECLARE #userParam VARCHAR(100)
,#statusParam VARCHAR(100)
SET #userParam = '%'
SET #statusParam = '%'
SELECT ROW_NUMBER() OVER (
ORDER BY PDT.[Name] DESC
) AS 'RowNumber'
,PDT.[Name] AS Project
,(
CASE WHEN (
STY.KanBanProductId IS NOT NULL
AND STY.SprintId IS NULL
) THEN 'KanBan' WHEN (
STY.KanBanProductId IS NULL
AND STY.SprintId IS NOT NULL
) THEN 'Sprint' END
) AS ProjectType
,STY.[Number] StoryNumber
,STY.Title AS StoryTitle
,TSK.[Name] AS Task
,CONVERT(VARCHAR(20), STY.Effort) AS Effort
,CONVERT(VARCHAR(20), TSK.OriginalEstimateHours) AS OriginalEstimateHours
,TSK.STATUS AS STATUS
FROM Task TSK
LEFT JOIN Story STY ON TSK.StoryId = STY.PK_Story
LEFT JOIN Sprint SPT ON STY.SprintId = SPT.PK_Sprint
LEFT JOIN Product PDT ON STY.ProductId = PDT.PK_Product
WHERE TSK.PointPerson LIKE #userParam
AND TSK.STATUS LIKE #statusParam
GROUP BY STY.[Number]
,TSK.STATUS
,STY.Title
,PDT.[Name]
,TSK.CreateDate
,TSK.[Name]
,STY.KanBanProductId
,STY.SprintId
,TSK.OriginalEstimateHours
,STY.Effort
My issue that that although I have the ORDER BY sorting by story number first it is not returning as expected (below is column STY.[Number]):
As you can see it foes from 33 to 4 to 42, I want it in numerical order so that 4 would be between 3 and 5 not 33 and 42. How do I achieve this?
Given the structure of your data (with a constant prefix), probably the easiest way to get what you want is:
order by len(STY.[Number]), STY.[Number]
This orders first by the length and then by the number itself.
Those are strings. Do you really expect SQL Server to be able to identify that there is a number at character 6 in every single row in the result, and instead of ordering by character 6, they pretend that, say, SUPP-5 is actually SUPP-05? If that worked for you, people who expect the opposite behavior (to treat the whole string as a string) would be complaining. The real fix is to store this information in two separate columns, since it is clearly two separate pieces of data.
In the meantime, you can hack something, like:
ORDER BY LEFT(col, 4), CONVERT(INT, SUBSTRING(col, 6, 255)));
As Martin explained, this should be on the outer query, not just used to generate a ROW_NUMBER() - generating a row number alone doesn't guarantee the results will be ordered by that value. And this will only work with additional checks to ensure that every single row has a value following the dash that can be converted to an int. As soon as you have SUPP-5X this will break.
It's sorting by the string in lexicography order. To get numerical ordering you need to extract the number from the string (with substring()) and cast it to integer.

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