I'm trying to query a table which has a column that contains straight xml data. Within the query I'm querying columns that hold straight data (int, vchar etc) but I'm also querying the xml column. In the xml column i want to grab a value within the xml and return null if it doesn't exist. I have the following query that almost works but returns duplicates. Need help!
I have my root xml CodeFiveReport then within it Properties and within that Property which has a serial number. I'm trying to grab the serial number if it exists and displaying it.
select Distinct rs.Id
, rs.CaseNumber
, rs.StartDate
, rs.[Status]
, rs.PatrolDistrict
, rs.PrimaryUnit
, rs.Location
, rs.ReportType
, rs.IncidentType
, rs.UserId
, rs.UnitId
, rs.UnitCode
, rs.IsLocked
, rs.LockedBy
, rs.AgencyId
, rl.ReportName
, rl.ParentId
, TempTable.Party.value('(SerialNumber/text())[1]', 'varchar(50)') as SerialNumber
from dbo.vw_ReportSummary rs OUTER APPLY Report.nodes('/CodeFiveReport/Properties/Property') AS TempTable(Party)
left outer join dbo.ReportLookup rl on rs.Id = rl.Id
where rs.[Status] = 'Approved'
order by rs.Id
Well, I was able to solve the problem
I changed Report.nodes('/CodeFiveReport/Properties/Property') to Report.nodes('/CodeFiveReport/Properties')
In turn I also changed my TempTable query to: TempTable.Party.value('(Property/SerialNumber/text())[1]', 'varchar(50)') as SerialNumber and that seemed to fix the duplicates.
Thanks for your help everybody.
Hard to say without knowing your exact database schema. Assuming that this is T-SQL: Have a look at CTE (common table expressions) and split your statement in two steps. That makes these kind of statements usually much simpler and often more efficient.
Related
I am struggling with trying to apply a date filter to my query. I keep getting this error message
Conversion failed when converting the varchar value 'Collect_Date' to
data type int
Here is my code:
SELECT
Location_ID,
CONVERT(Date,CONVERT(varchar(10),Collect_Month_Key,101)) as 'Collect_Date',
Calc_Gross_Totals, Loc_Country,
CONVERT(varchar(8),Collect_Month_Key)+'-'+Location_ID as 'Unique Key'
FROM
FT_GPM_NPM_CYCLES,
LU_Location,
LU_Loc_Country
WHERE
LU_Location.LU_Loc_Country_Key=LU_Loc_Country.LU_Loc_Country_Key
AND FT_GPM_NPM_CYCLES.Lu_Loc_Key= LU_Location.LU_Loc_Key
AND Collect_Month_Key<>-1
AND 'Collect_Date'>=2016-1-1
ORDER BY
Location_ID,
Collect_Date;
If someone could help that would be appreciated. I am also getting a different error when I try to do the Month(Collect_Date). So if anyone knows why on that I would appreciate it. I have attched a picture with the code nd results I am getting.
I see whats going on, you are trying to use the alias in the select statement. You can't do that, There are a few other issues that have been covered in the comments, but here is the immediate answer to the question:
Select Location_ID
, Convert(Date,CONVERT(varchar(10),Collect_Month_Key,101)) as Collect_Date
, Calc_Gross_Totals
, Loc_Country
, CONVERT(varchar(8),Collect_Month_Key)+'-'+Location_ID as [Unique Key]
From FT_GPM_NPM_CYCLES
, LU_Location
, LU_Loc_Country
Where LU_Location.LU_Loc_Country_Key=LU_Loc_Country.LU_Loc_Country_Key
and FT_GPM_NPM_CYCLES.Lu_Loc_Key= LU_Location.LU_Loc_Key
and Collect_Month_Key <> -1
and Convert(Date,CONVERT(varchar(10),Collect_Month_Key,101)) >= '2016-1-1'
Order By Location_ID, Collect_Date;
Here is an updated query that brings following modifications:
As commented by Robert Sheahan, you cannot use a resultset column alias in the WHERE clause
As commented by Larnu, since you are storing dates as strings, you could simply do string comparaison to filter records (and return string values). With this technique, you do not need additional condition Collect_Month_Key <> -1, since string '-1' is not greater than string '20160101'.
use explicit joins instead of implicit joins (comment by Gordon Linoff)
I added table aliases : they make the query easier to read (and make it possible to self-join a table...)
I would also recommend to to prefix all columns being used in the query with their table alias. This clearly indicates from which table each column comes from, and makes the query easier to understand and maintain. NB: if Collect_Month_Key belongs to a table other than FT_GPM_NPM_CYCLES, you want to move the condition from the WHERE clause to the ON clause of the relevant JOIN)
Query:
SELECT
Location_ID,
Collect_Month_Key AS Collect_Date,
Calc_Gross_Totals,
Loc_Country,
CONVERT(varchar(8),Collect_Month_Key) + '-' + Location_ID AS Unique_Key
FROM
FT_GPM_NPM_CYCLES AS cyc
INNER JOIN LU_Location AS loc
ON cyc.Lu_Loc_Key = loc.LU_Loc_Key
INNER JOIN LU_Loc_Country AS cty
ON loc.LU_Loc_Country_Key = cty.LU_Loc_Country_Key
WHERE
Collect_Month_Key > '20160101'
ORDER BY
Location_ID,
Collect_Month_Key
To answer your comment "So if I don't put the collect_Date in the WHERE, where should I put it for something like this in the future?", I suggest Common Table Expressions. Functionally they are equivalent to defining a derived table in the FROM clause, but they move it "above" so it feels more like "before" and I think they make it much easier to read. To convert GMB's excellent solution to using a CTE:
--Leading ; because CTEs require prvious command terminated explicitly
;WITH cteWithDates as ( --cteDates becomes a virtual temporary table
SELECT
cyc.* --Keep all the original columns of FT_GPM_NPM_CYCLES
, Collect_Month_Key AS Collect_Date --and add Collect_Date and Unique_Key
, CONVERT(varchar(8),Collect_Month_Key) + '-' + Location_ID AS Unique_Key
FROM FT_GPM_NPM_CYCLES AS cyc
) --you could add more CTEs with the following format,
--all become available at the end
--, cteMore as (SELECT ... FROM ...)
--the first line after the closing ) has access to all CTEs, but ONLY that line
SELECT Location_ID,
Collect_Date,
Calc_Gross_Totals,
Loc_Country,
Unique_Key
FROM
cteWithDates AS cyc --Use the CTE as you would your original table,
--but the added fields are now available EVERYWHERE in your query!
INNER JOIN LU_Location AS loc
ON cyc.Lu_Loc_Key = loc.LU_Loc_Key
INNER JOIN LU_Loc_Country AS cty
ON loc.LU_Loc_Country_Key = cty.LU_Loc_Country_Key
WHERE
Collect_Date > '20160101' --NOW you can use CollectDate!
ORDER BY
Location_ID,
Collect_Date --And here too
Note that this is much more efficient than defining an actual temporary table with #TableName, because the query optimizer can drop unused records from the CTE but it has to put them all into the #temporary table, a huge performance difference if your table is large and the matching subset small.
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've created an Oracle SQL query which links to about five tables I'm using in an Oracle FROM clause to an Oracle Form but the problem with the query is that some records are duplicated, so I only want to show one line in the form and not any duplicate records. I've tried GROUP BY and PARTITION BY statements but the query becomes to slow with adding this into the statement.
I'm now thinking of doing this as a procedure and bring back just one of the duplicates if any occur. Would it be best to bring back an ORACLE table of records from the database into the form? How would it be best to look for a duplicate in an Oracle PL/SQL loop?
I've updated the question and adde the full query below to explain it better. The surr_id the first column in select statement below is unique but what I want to show in the Oracle form is the production number along with the other columns which are not unique. There can be duplicates of production number and even sometimes three production number records the same. Hope this helps. I was thinking of putting this in a loop and just grabbing the first production number and then only bringing back each record when the production number changes.
select x.surr_id ,
x.supplier_name as supplier ,
x.broadcaster_name as broadcaster ,
ptle.title as production_title ,
x.production_number as production_number ,
stle.title as series_title ,
x.production_source as supplied_source_ind ,
x.third_party_group_id ,
x.bro_broadcast_by_tp_surr_id ,
x.station_id from (select usage_headers.surr_id as surr_id ,
broad_supp.supplier_name as supplier_name ,
broad_supp.broadcaster_name as broadcaster_name ,
usage_headers.production_number as production_number ,
productions.production_source as production_source ,
broad_supp.station_id as station_id ,
usage_headers.prod_exploitation_cre_surr_id as prod_exploitation_cre_surr_id ,
usage_headers.bro_broadcast_by_tp_surr_id as bro_broadcast_by_tp_surr_id ,
productions.cre_surr_id as cre_surr_id ,
productions.prod_series_cre_surr_id as prod_series_cre_surr_id ,
broad_supp.third_party_group_id as third_party_group_id
from usage_headers, productions, (SELECT /*+ index (bro bro_pk) */
third_party.surr_id AS THIRD_PARTY_SURR_ID,
third_party.supplier_group_id AS THIRD_PARTY_GROUP_ID,
third_party.dn_root_tp_surr_id AS THIRD_PARTY_ROOT_ID,
third_party.supplier_name, bro.station_id AS STATION_ID,
bro.dn_tp_name AS BROADCASTER_NAME FROM ( SELECT tp.surr_id,
tp.name AS supplier_name,
tp.tp_surr_id AS supplier_group_id,
tp.dn_root_tp_surr_id FROM third_parties tp
CONNECT BY PRIOR tp.surr_id = tp.tp_surr_id
START WITH tp.surr_id IN (4251, 4247, 4237, 4034, 10157, 14362, 9834)) third_party
JOIN broadcasters bro ON (third_party.surr_id = bro.tp_surr_id)) broad_supp
where broad_supp.THIRD_PARTY_SURR_ID = usage_headers.bro_broadcast_by_tp_surr_id
AND usage_headers.prod_exploitation_cre_surr_id = productions.cre_surr_id
and usage_headers.prod_exploitation_cre_surr_id IS NOT NULL
and usage_headers.right_type in ('M','B')
AND usage_headers.udg_surr_id IS NOT NULL
AND NVL(usage_headers.dn_uls_usage_status,'3') NOT IN ('9', '11')
AND productions.production_source <> 'AP') x
LEFT OUTER JOIN titles ptle ON ( ptle.cre_surr_id = x.cre_surr_id AND ptle.tt_code = 'R')
LEFT OUTER JOIN titles stle ON ( stle.cre_surr_id = x.prod_series_cre_surr_id AND stle.tt_code = 'R')
thanks Guys in Advance
If you're getting records that are entirely duplicated then just adding a DISTINCT clause, so your SELECT becomes SELECT DISTINCT will ensure that only one of the records is returned. If even one column is different though then this won't work.
For simplicity, assume I have two tables joined by account#. The second table has two columns, id and comment. Each account could have one or more comments and each unique comment has a unique id.
I need to write a t-sql query to generate one row for each account - which I assume means I need to combine as many comments as might exit for each account. This assumes the result set will only show the account# once. Simple?
Sql Server is a RDBMS best tuned for storing data and retrieving data, you can retrieve the desired data with one very simple query but the desired format should be handled with any of the reporting tools available like ssrs or crystal reports
Your query will be a simple inner join something like this
SELECT A.Account , B.Comment
FROM TableA AS A INNER JOIN TableB AS B
ON A.Account = B.Account
Now you can use your reporting tool to Group all the Comments by Account when Displaying data.
I do agree with M. Ali, but if you don't have that option, the following will work.
SELECT [accountID]
, [name]
, (SELECT CAST(Comment + ', ' AS VARCHAR(MAX))
FROM [comments]
WHERE (accountID = accounts.accountID)
FOR XML PATH ('')
) AS Comments
FROM accounts
SQL Fiddle
In my actual project I have this exact situation.
What you need is a solution to aggregate the comments in order to show only one line per account#.
I solve it by creating a function to concatenate the comments, like this:
create function dbo.aggregateComments( #accountId integer, #separator varchar( 5 ) )
as
begin;
declare #comments varchar( max ); set #comments = '';
select #comments = #comments + #separator + YouCommentsTableName.CommentColumn
from dbo.YouCommentsTableNAme
where YouCommentsTableName.AccountId = #accountId;
return #comments;
end;
You can use it on you query this way:
select account#, dbo.aggretateComments( account#, ',' )
from dbo.YourAccountTableName
Creating a function will give you a common place to retrieve your comments. It's a good programming practice.
I'm using parts of a replication script written by a well known blogger. I want to make the part I listed below add 1 more column from a totally different table that only holds 1 row. Basically that table with a single row has a site name on it, and I want that site name from that table to populate as part of this INSERT INTO.
I know SQL 2005 introduced OUTER APPLY, but I am not sure if that is the best method to go with. Any sugegstions are welcome. Thanks.
Insert Into dbo.dba_replicationMonitor
(
monitorDate
, publicationName
, publicationDB
, iteration
, tracer_id
, distributor_latency
, subscriber
, subscriber_db
, subscriber_latency
, overall_latency
, SiteNameFromSiteInfoTable --Need to add this
)
Select
#currentDateTime
, #publicationToTest
, #publicationDB
, iteration
, tracer_id
, IsNull(distributor_latency, 0)
, subscriber
, subscriber_db
, IsNull(subscriber_latency, 0)
, IsNull(overall_latency,
IsNull(distributor_latency, 0) + IsNull(subscriber_latency, 0
)
, sitename = 'SELECT sitename FROM tblSiteInfo' --need this query to insert as well
)
From #tokenResults;
I was thinking of a variable but I don't thnk passing the variable will be enough. Any help is greatly appreciated. Thanks.
You can just join to the second table as normal. If there's only one row in this other table (and will only ever be one row), it's not going to double your results. So, like this:
INSERT INTO dbo.dba_replicationMonitor (_column_list_)
SELECT _#ToeknResultsColumns_, b.sitename
FROM #TokenResults as a
JOIN tblSiteInfo as b
ON 1 = 1