I have a working query that contains a large number of variable IDs. Rather than copying and pasting in each ID whenever I need to run a new query, I was wondering if there was a way to create a stored procedure out of the query below and pass in a group of IDs?
Here is the query. The IDs change all the time, so I'm trying to figure out a way of doing this easier but I'm not having much luck.
I thought about using a cursor in a stored procedure and just passing each ID, but that seems cumbersome and inefficient.
SELECT gm.geoId, T.number As surveyID, 0 as SpeciesCount
FROM (
VALUES (1994328036),(1994328037),(1994328038),(1994328039),(1994328040),(1994328041),(1994328042),(1994328043),
(1994328044),(1994328045),(1994328046),(1994328047),(1994328048),(1994328049),(1994328050),(1994328051),
(1994328052),(1994328053),(1994328054),(1994328055)
) AS T(number)
CROSS JOIN dbo.groupBiology gm
You can create a table-valued function (TVF) like this:
CREATE FUNCTION tvf_GetIDs ()
RETURNS
#output TABLE ( data int )
AS
BEGIN
INSERT INTO #output (data) VALUES
(1994328036),(1994328037),(1994328038),(1994328039),
(1994328040),(1994328041),(1994328042),(1994328043),
(1994328044),(1994328045),(1994328046),(1994328047),
(1994328048),(1994328049),(1994328050),(1994328051),
(1994328052),(1994328053),(1994328054),(1994328055)
RETURN
END
GO
then use this function wherever the IDs are required, e.g.
SELECT *
FROM Customers AS c
INNER JOIN (SELECT * FROM tvf_GetIDs()) t ON c.CustID = t.data
You only need to update the TVF whenever the IDs change.
Related
I have a table-value function that takes an ID number of a person and returns a few rows and columns. In another query, I am creating a SELECT that retrieves a lot of information about many people. How can I pass an id number from my main query to my function to sum a column and join it to my main query? I wish I didn't have a table value function since that would work easily, however, this function is used elsewhere and I'd like to reuse it. Perhaps this isn't even possible with a table-value function and I need to create a scalar one.
My main Query looks like this:
select id_num, name, balance
from listOfPeople
And the table-value function looks like this:
calculatePersonalDiscount(id_number)
I would like to do something like:
select id_num, name, balance
from listOfPeople
left join
(
SELECT id_num, SUM(discount)
FROM calculatePersonalDiscount(listOfPeople.id_num)
) x ON x.id_num = listOfPeople.id_num
But you can't pass listOfPeople.id_num into the function since it's not really the same scope.
In SQL Server 2005 you can use the CROSS APPLY syntax:
select id_num, name, balance, SUM(x.discount)
from listOfPeople
cross apply dbo.calculatePersonalDiscount(listOfPeople.id_num) x
Likewise there's an OUTER APPLY syntax for the equivalent of a LEFT OUTER join.
I needed this badly and you gave me the start, and here I was able to JOIN on another View. (another table would work too), and I included another function in the WHERE clause.
SELECT CL_ID, CL_LastName, x.USE_Inits
FROM [dbo].[tblClient]
JOIN dbo.vw_InsuranceAppAndProviders ON dbo.vw_InsuranceAppAndProviders.IAS_CL_ID = tblClient.CL_ID
CROSS APPLY dbo.ufx_HELPER_Client_CaseWorker(tblClient.CL_ID) x
WHERE dbo.vw_InsuranceAppAndProviders.IAS_CL_ID = tblClient.CL_ID
AND dbo.ufx_Client_IsActive_By_CLID(tblClient.CL_ID) = 1
AND (dbo.vw_InsuranceAppAndProviders.IAS_InsuranceType = 'Medicare')
I am consolidating a web service. I am replacing multiple calls to the service with one call that contains the data.
I have created a table:
CREATE TABLE InvResults
(
Invoices nvarchar(max),
InvoiceDetails nvarchar(max),
Products nvarchar(max)
);
I used (max) because I don't know how complex the json will get at this time.
I need to do some sort of selects like this (this is pseudocode, not actual SQL):
SELECT
(SELECT *
INTO InvResults for Column Invoices
FROM MyInvoiceTable
WHERE SomeColumns = 'someStuffvariable'
FOR JSON PATH, ROOT('invoices')) AS invoices;
SELECT
(SELECT *
INTO InvResults for Column InvoiceDetails
FROM MyInvoiceDetailsTable
WHERE SomeColumns = 'someStuffvariable'
FOR JSON PATH, ROOT('invoicedetails')) AS invoicedetails;
I don't know how to format this and my google skills are failing me at this point. I understand that I probably want to use an UPDATE statement, but I'm not sure how to do this in combination with the rest of my requirements. I'm exploring How do I UPDATE from a SELECT in SQL Server? but I am still at a halt.
The end result should be a table "InvResults" that has 3 columns containing one row with results from Select statements as JSON. The column names should be defined the same as the json root objects.
INSERT INTO InvResults(Invoices,InvoidesDetails)
SELECT
(SELECT *
INTO InvResults for Column Invoices
FROM MyInvoiceTable
WHERE SomeColumns = 'someStuffvariable'
FOR JSON PATH, ROOT('invoices'))
,
(SELECT *
INTO InvResults for Column InvoiceDetails
FROM MyInvoiceDetailsTable
WHERE SomeColumns = 'someStuffvariable'
FOR JSON PATH, ROOT('invoicedetails'))
;
Because the SELECT.. FOR JSON is only returning 1 row above works.
The third field is easily to added, but left to do for yourself 😉
Here's the code structure I'm looking at:
CREATE VIEW [dbo].[View1]
AS SELECT t1.[ID] ,
udf1.[Column1] ,
udf1.[Column2] ,
udf1.[Column3]
FROM [Table1] t1
CROSS APPLY [dbo].[UDF1] ( [dbo].[UDF2] ( t1.[ID] ) ) udf1
GO
[dbo].[UDF1] and [dbo].[UDF2] are both inline table valued functions.
I don't remember the exact details, but SSMS gave me errors when I tried to use JOIN, and SO told me I needed to use CROSS APPLY to fix it - I think that was the right choice though?
Anyway, the main issue I have right now is:
Cannot find either column "dbo" or the user-defined function or aggregate "dbo.UDF2", or the name is ambiguous.
... despite the fact that UDF2 clearly exists: I can see it in SSMS.
I suspect the real problem is the way UDF2 is passed as a parameter into UDF1.
UDF2 returns a table, and the columns exactly match the table-valued parameter defined for UDF1.
But I think SQL Server doesn't support this syntax?
Do I need to convert my view into a multi-line table-valued function, declare a table variable to receive the results of UDF2, and then pass that table variable into UDF1?
(P.S. SQL Server's errors are some of the least helpful I've ever seen, in any language ...)
This will probably have really poor performance, but I think you need to use a separate CROSS APPLY for each table-value function. I don't think it is possible to use the contents returned by UDF2 in UDF1, since UDF1 would need a parameter of Table Type and UDF2 returns a Table but not a Table Type.
Functions cannot return Table Types, so you will probably have to find another solution. You could create yet another function that inserts the results of UDF2 into a Table Type and then calls UDF1.
If you could do it, it would look something like:
CREATE VIEW [dbo].[View1]
AS SELECT t1.[ID] ,
udf1.[Column1] ,
udf1.[Column2] ,
udf1.[Column3]
FROM [Table1] t1
CROSS APPLY [dbo].[UDF2] ( t1.[ID] ) udf2
CROSS APPLY [dbo].[UDF1] ( udf2 ) udf1
GO
To create "yet another wrapper function" it would look like:
CREATE FUNCTION [dbo].[UDF3](
#id INT -- data type for t1.ID
) RETURNS #t TABLE (
-- ... Your columns here ...
)
AS
BEGIN
-- Your user defined Table Type
DECLARE #udf2 UDF2_TABLE
INSERT #udf2 (... columns ...)
SELECT udf2.* -- try to use exact column names instead of *
FROM [Table1] t1
CROSS APPLY [dbo].[UDF2]( t1.[ID] )
-- I'm not really sure this is possible either
INSERT #t (... columns ...)
SELECT udf1.*
FROM [dbo].[UDF1](#udf2) udf1
RETURN;
END
I have 2 tables with these columns:
CREATE TABLE #temp
(
Phone_number varchar(100) -- example data: "2022033456"
)
CREATE TABLE orders
(
Addons ntext -- example data: "Enter phone:2022033456<br>Thephoneisvalid"
)
I have to join these two tables using 'LIKE' as the phone numbers are not in same format. Little background I am joining the #temp table on the phone number with orders table on its Addons value. Then again in WHERE condition I am trying to match them and get some results. Here is my code. But my results that I am getting are not accurate. As its not returning any data. I don't know what I am doing wrong. I am using SQL Server.
select
*
from
order_no as n
join
orders as o on n.order_no = o.order_no
join
#temp as t on t.phone_number like '%'+ cast(o.Addons as varchar(max))+'%'
where
t.phone_number = '%' + cast(o.Addons as varchar(max)) + '%'
You can not use LIKE statement in the JOIN condition. Please provide more information on your tables. You have to convert the format of one of the phone field to compile with other phone field format in order to join.
I think your join condition is in the wrong order. Because your question explicitly mentions two tables, let's stick with those:
select *
from orders o JOIN
#temp t
on cast(o.Addons as varchar(max)) like '%' + t.phone_number + '%';
It has been so long since I dealt with the text data type (in SQL Server), that I don't remember if the cast() is necessary or not.
Instead of trying to do everything in a single top-level query, you should apply a transformation projection to your orders table and use that as a subquery, which will make the query easier to understand.
Using the CHARINDEX function will make this a lot easier, however it does not support ntext, you will need to change your schema to use nvarchar(max) instead - which you should be doing anyway as ntext is deprecated, fortunately you can use CONVERT( nvarchar(max), someNTextValue ), though this will reduce performance as you won't be able to use any indexes on your ntext values - but this query will run slowly anyway.
SELECT
orders2.*,
CASE WHEN orders2.PhoneStart > 0 AND orders2.PhoneEnd > 0 THEN
SUBSTRING( orders2.Addons, orders2.PhoneStart, orders2.PhoneEnd - orders2.PhoneStart )
ELSE
NULL
END AS ExtractedPhoneNumber
FROM
(
SELECT
orders.*, -- never use `*` in production, so replace this with the actual columns in your orders table
CHARINDEX('Enter phone:', Addons) AS PhoneStart,
CHARINDEX('<br>Thephoneisvalid', AddOns, CHARINDEX('Enter phone:', Addons) ) AS PhoneEnd
FROM
orders
) AS orders2
I suggest converting the above into a VIEW or CTE so you can directly query it in your JOIN expression:
CREATE VIEW ordersWithPhoneNumbers AS
-- copy and paste the above query here, then execute the batch to create the view, you only need to do this once.
Then you can use it like so:
SELECT
* -- again, avoid the use of the star selector in production use
FROM
ordersWithPhoneNumbers AS o2 -- this is the above query as a VIEW
INNER JOIN order_no ON o2.order_no = order_no.order_no
INNER JOIN #temp AS t ON o2.ExtractedPhoneNumber = t.phone_number
Actually, I take back my previous remark about performance - if you add an index to the ExtractedPhoneNumber column of the ordersWithPhoneNumbers view then you'll get good performance.
I have a table which looks something like
Event ID Date Instructor
1 1/1/2000 Person 1
1 1/1/2000 Person 2
Now what I want to do is return this data so that each event is on one row and the Instructors are all in one column split with a <br> tag like 'Person 1 <br> Person 2'
Currently the way I have done this is to use a function
CREATE FUNCTION fnReturnInstructorNamesAsHTML
(
#EventID INT
)
RETURNS VARCHAR(max)
BEGIN
DECLARE #Result VARCHAR(MAX)
SELECT
#result = coalesce(#result + '<br>', '') + inst.InstructorName
FROM
[OpsInstructorEventsView] inst
WHERE
inst.EventID = #EventID
RETURN #result
END
Then my main stored procedure calls it like
SELECT
ev.[BGcolour],
ev.[Event] AS name,
ev.[eventid] AS ID,
ev.[eventstart],
ev.[CourseType],
ev.[Type],
ev.[OtherType],
ev.[OtherTypeDesc],
ev.[eventend],
ev.[CourseNo],
ev.[Confirmed],
ev.[Cancelled],
ev.[DeviceID] AS resource_id,
ev.Crew,
ev.CompanyName ,
ev.Notes,
dbo.fnReturnInstructorNamesAsHTML(ev.EventID) as Names
FROM
[OpsSimEventsView] ev
JOIN
[OpsInstructorEventsView] inst
ON
ev.EventID = inst.EventID
This is very slow, im looking at 4seconds per call to the DB. Is there a way for me to improve the performance of the function? Its a fairly small function so im not sure what I can do here, and I couldnt see a way to work the COALESCE into the SELECT of the main procedure.
Any help would be really appreciated, thanks.
You could try something like this.
SELECT
ev.[BGcolour],
ev.[Event] AS name,
ev.[eventid] AS ID,
ev.[eventstart],
ev.[CourseType],
ev.[Type],
ev.[OtherType],
ev.[OtherTypeDesc],
ev.[eventend],
ev.[CourseNo],
ev.[Confirmed],
ev.[Cancelled],
ev.[DeviceID] AS resource_id,
ev.Crew,
ev.CompanyName ,
ev.Notes,
STUFF((SELECT '<br>'+inst.InstructorName
FROM [OpsInstructorEventsView] inst
WHERE ev.EventID = inst.EventID
FOR XML PATH(''), TYPE).value('.', 'nvarchar(max)'), 1, 4, '') as Names
FROM
[OpsSimEventsView] ev
Not sure why you have joined OpsInstructorEventsView in the main query. I removed it here but if you needed you can just add it again.
A few things to look at:
1) The overhead of functions makes them expensive to call, especially in the select statement of a query that could potentially be returning thousands of rows. It will have to execute that function for every one of them. Consider merging the behavior of the function into your main stored procedure, where the SQL Server can make better use of its optimizer.
2) Since you are joining on event id in both tables, make sure you have an index on those two columns. I would expect that you do, given that those both appear to be primary key columns, but make sure. An index can make a huge difference.
3) Convert your coalesce call into its equivalent case statements to remove the overhead of calling that function.
Yes make it an INLINE Table-Valued SQL function:
CREATE FUNCTION fnReturnInstructorNamesAsHTML
( #EventID INT )
RETURNS Table
As
Return
SELECT InstructorName + '<br>' result
FROM OpsInstructorEventsView
WHERE EventID = #EventID
Go
Then, in your SQL Statement, use it like this
SELECT ]Other stuff],
(Select result from dbo.fnReturnInstructorNamesAsHTML(ev.EventID)) as Names
FROM OpsSimEventsView ev
JOIN OpsInstructorEventsView inst
ON ev.EventID = inst.EventID
I'm not exactly clear how the query you show in your question is concatenating data from multiple rows in one row of the result, but the problem is that ordinary UDFs are compiled on use, on EVERY use, so for each row in your output result the Query processopr has to recompile the UDF again. THis is NOT True for an "inline table valued" UDF, as it's sql is folded into the outer sql before it is passed to the SQL optimizer, (the subsystem that generates the statement cache plan) and so the UDF is only compiled once.