SQL: querying specific value in column with XML Data - sql

Boy, I've been researching this subject, but I am just not getting it. Sorry if I'm asking a question that has all ready been asked a million times, but its hard to understand when you're a noob like me, and the values in my tables aren't like the others I've seen. So here it goes...
In my SQL Server database, I have a table that has all my listings in it, called ItemsEbay.
The main identifier for each individual item has a column ID value called ItemID, so I would like to refer to that as needed.
Within that table is a column named ItemSpecifics that contains XML data.
Within the ItemSpecifics XML Data, is a node with a <Name> of UPC text and random value for that node:
What I would like, is a query that would allow me to search all the items in the ItemsEbay table that have a specific UPC value of my choosing, such as 1000100 or 10U100 for instance.
When I find the match values I'm querying, I would like to be able to replace them all at once with a new value Does Not Apply.

First of all: do not poste pictures!
What I've done here is your job: How to create a MCVE!
A dummy table with two rows, one contains the searched value, one doesn't
DECLARE #dummyTable TABLE(ID INT IDENTITY, ItemSpecifications XML);
INSERT INTO #dummyTable VALUES
(N'<SelectedValues>
<SelectedValue>
<Name>TestName</Name>
<Value>Xml1</Value>
</SelectedValue>
<SelectedValue>
<Name>UPC</Name>
<Value>123</Value><!--The UPC named value = 123 -->
</SelectedValue>
</SelectedValues>')
,(N'<SelectedValues>
<SelectedValue>
<Name>TestName</Name>
<Value>Xml2</Value>
</SelectedValue>
<SelectedValue>
<Name>UPC</Name>
<Value>999</Value><!--The UPC named value = 999 -->
</SelectedValue>
</SelectedValues>');
--I search for "123" and want to replace it with "SomeOther"
DECLARE #SearchFor VARCHAR(100)='123';
DECLARE #ReplaceWith VARCHAR(100)='SomeOther';
--The update statement uses .modify() for the XML change and .exist() to check for the search value below a <SelectedValue>, which <Name> element has a text() of "123":
UPDATE #dummyTable SET ItemSpecifications.modify(N'replace value of (/SelectedValues
/SelectedValue[(Name/text())[1]="UPC"]
/Value/text())[1]
with sql:variable("#ReplaceWith")')
WHERE ItemSpecifications.exist(N'/SelectedValues
/SelectedValue[(Name/text())[1]="UPC"]
/Value[text()=sql:variable("#SearchFor")]')=1;
--Check the result
SELECT * FROM #dummyTable;

Related

Using column names stored in another source [duplicate]

This question already has an answer here:
How to create a table using column names present in another table?
(1 answer)
Closed last year.
I am trying to figure out a way to name my columns in a newly declared table by fetching the name from another source like another string or value in another table.
What I want to know is something like this is possible or not?
In below code I have declared a string and entered a value.
I want this value to be the column name in my table below.
Can anyone help me fix this?
declare #string3 varchar(max);
set #string3 = 'LIST_TYPE';
select #string3;
DECLARE #TABP TABLE
(
id int,
#string3 varchar(max)
)
In case you need a temp table with a structure, which includes only columns from your another source, you can do smth like this:
SELECT column1, ..., columnN
INTO #tmp
FROM Source
WHERE 1=0
But you won't be able to specify column's datatypes and constraints, they will be inherited from your Source table.

Add data behind string based on data in other column in SQL Server

I have the following challenge which I can't solve at the moment:
I have a column (LongTextfield1) in, let's say, table 'Data' that contains HTML data. Each row in table 'Data' is basically a document with text and pictures. In several occassions multiple pictures are included in the same column (LongTextfield1). In case of a picture, the following code will be shown that refers to the picture:
\Download.aspx?DocumentID={SomeGUID}
To make it a bit more challenging, this piece of code can be included in the same column multiple times, depending on how much pictures are included in the HTML (document). All pictures have their own GUID. So far, so good. This is already present in the column.
In the application that I would like to show these pictures I also need a 'RecordID'. This is a unique ID per picture. So, outcome should be:
\Download.aspx?DocumentID={SomeGUID}&Recorid=SomeID
I have a table 'DocumentData' that contains the unique GUID (DocumentID) and RecordID for each picture. So, I know which RecordID belongs to each GUID.
I need a stored procedure (or other automatic mechanism) that adds the RecordID behind the corresponding GUID. I have tried to use REPLACE '{SomeGUID}' for {SomeGUID}&RecorID=SomeID' but that didn't work. Below is the stored procedure, that is fed with data from the 'DocumentData' table. It keeps on replacing the same string with every possible GUID+RecordID combination: (DocumentID={SomeGUID}&RecordID=1{SomeGUID2}&RecordID2.. etc. and that's not what I want. #DocumentID and #Origin (RecorID) in the stored procedure refer to the 'DocumentData' table:
CREATE PROCEDURE [dbo].[Replace_Picture2] (
#DocumentID UNIQUEIDENTIFIER,
#Origin varchar(100)
) AS
BEGIN
update
data
set longtextfield1 = REPLACE(CAST(LongTextField1 AS VARCHAR(max)), ltrim(cast(#DocumentID as varchar(100))) + '}', ltrim(cast(#DocumentID as varchar(100))) + '}' + '&RecordID=' + #Origin + '"')
where 1=1
and pageid = 1
and groupid = 2
and subgroupid = 8
END
GO
How can I achieve to add the RecordID behind the DocumentID, based on the combinations present in table 'DocumentData'? Thanks for your help!
This is solved by replacing ‘#DocumentID’ + ‘}”’ (notice the ‘}”’ instead of ‘}’. This prevents the stored procedure to keep on replacing and replacing..

Matrix table index SQL Server 2008

I have a table with two columns built from another table of names, one identity and one a name like this:
ID---Name
1----Mike
2----Jeff
3----Robert
...down to however many
Could be 10 rows, could be 100. This will vary depending on input from other tables that are always changing but never be over 160 or so.
Now, pairings of names will have some meaning and thus a decimal data type score will be associated with said pairing (how at this point doesn’t matter, just need to build it for now...numbers just illustrative). I envision a matrix kind of like this:
ID------Name------Mike-------Jeff--------Robert-------- ...out to however many
1 -------Mike-------NULL------100.1------5.4-------- ...out to however many
2 -------Jeff---------100.1------NULL-----21.23--------- ...out to however many
3 ------Robert-------5.4--------21.23-----NULL---------...out to however many
…down to however many happen to be in the first table…
Maybe this isn’t quite the most optimal way to go (Yes, I know there are duplicates in the table but I plan to structure the queries such that the duplicates are ignored) but at this point am not aware of many viable options. After searching around, I thought maybe I wanted a pivot but that doesn’t seem to fit what I have here because I’m leaving the names in the column and associating them as column heads for a paired score. Then I thought maybe I wanted to store a variable as the value of each row and then add them as the columns. That was no help. My latest iteration was maybe creating a temp table as an exact copy with and identity column, then trying to select the specific name by the identity and looping through them but I can’t even seem to grab the first name and make it a column name in addition to a row value under the name column...see below
--create a table of names with an identity column
CREATE TABLE myTable2
(
ID INT IDENTITY(1,1),
Name VARCHAR(5),
);
--add names to the table from a different table
INSERT INTO myTable1 (Name)
SELECT Name
FROM myTable1
--create a temp table with the same values
SELECT ID, Name
INTO #new
FROM myTable2
GROUP BY ID, Name
--insert name from first row as a column head
INSERT INTO myTable2 (SELECT Number FROM #new WHERE ID =1)
So, in the last bit there, INSERT INTO”, I want to copy the names, in this instance “Mike” and make it ALSO a column head in the same table where it is a row (like in my second table). I get an error message that the syntax is not correct for the statement. Why isn’t this allowed? How can I get it to do what I want? It also has been suggested by someone that knows way more about this stuff than me, that maybe instead of building the table as a matrix, build it as below. It is possible here to get rid of the duplicates this way and I would except I have no idea where to even begin doing this…
Name1-----------Name2-----------Calculated Value
Mike--------------Mike-------------NULL
Jeff---------------Mike-------------100.1
Robert-------------Mike-------------5.4
Mike--------------Jeff-------------100.1
Jeff----------------Jeff-------------NULL
Robert------------Jeff-------------21.23
Mike--------------Robert-----------5.4
Jeff---------------Robert-----------21.23
Robert------------Robert-----------NULL
...etc
Any help suggestions or pointing of me in the right and most appropriate direction would be greatly appreciated!
EDIT: Here's how I solved my problem. Looks like the Cartesian product was the way to go. Thanks #Alex Kudryashev
--create a table of cross joined names
CREATE TABLE cartNames
(
Name1 VARCHAR(5),
Name2 VARCHAR(5),
);
--create two temporary tables from a source table of names
SELECT Name AS Name1
INTO #name1
FROM names
GROUP BY Name
SELECT Name AS Name2
INTO #Name2
FROM names
GROUP BY Name
--populate the Cartesian table
INSERT INTO cartNames
SELECT * FROM #name1 CROSS JOIN #name2
--get rid of the temp tables
DROP TABLE #Name1
DROP TABLE #Name2
--add columns and populate calculated scores
---
It looks like you want to create a Cartesian Product. There is very easy way to do so.
declare #tbl table(name varchar(10))
insert #tbl(name) values('MIke'),('Jeff'),('Robert')
select t1.name name1,t2.name name2, some_udf(t1.name,t2.name) calc_value
from #tbl t1 cross join #tbl t2

Get values based on newly inserted value using SQL

I want to make filtration on a column after selecting a specific value of another column in the same table, I tried to use #... special character followed by the column's name to get the address of this value.
My SQL statement is like the following :
SELECT ATTRIBUTE FROM TABLE WHERE FIELD = '#FIELDNAME';
If I used a specific value instead of #FIELDNAME, it will work properly but it will be static but I need it to be dynamic based on the selected value.
Create another table which will have the list of values that are in the FIELDNAME and give each record a unique id ,then retrieve the value depending on what you have selected by the name of the new table's field preceded by '#...'
I don't know if that what are you looking for, please let me know.
If no triggers are allowed, do you have any date/time column in the table? Is it possible to have that extra column anyway to see the time of a newly inserted row?
You may have to check the lastest row entered, save its field value into a variable. Then do the select based on the variable value.
Based on the vague last row id you could try the following (it's not pretty). But again, if you have date/time that's more accurate.
select attribute from table
where field = (select field from table
where rowid =(select max(rowid) from table))
;
upate
Do you have the priviledge to set up your insert command as below:
insert into table (id, col1, col2,...) values (1,'something', 'something',...)
returning id into variable; -- you may either save field or id depending on your table
Then you may use this variable to select the records you want.

SQL query select from table and group on other column

I'm phrasing the question title poorly as I'm not sure what to call what I'm trying to do but it really should be simple.
I've a link / join table with two ID columns. I want to run a check before saving new rows to the table.
The user can save attributes through a webpage but I need to check that the same combination doesn't exist before saving it. With one record it's easy as obviously you just check if that attributeId is already in the table, if it is don't allow them to save it again.
However, if the user chooses a combination of that attribute and another one then they should be allowed to save it.
Here's an image of what I mean:
So if a user now tried to save an attribute with ID of 1 it will stop them, but I need it to also stop them if they tried ID's of 1, 10 so long as both 1 and 10 had the same productAttributeId.
I'm confusing this in my explanation but I'm hoping the image will clarify what I need to do.
This should be simple so I presume I'm missing something.
If I understand the question properly, you want to prevent the combination of AttributeId and ProductAttributeId from being reused. If that's the case, simply make them a combined primary key, which is by nature UNIQUE.
If that's not feasible, create a stored procedure that runs a query against the join for instances of the AttributeId. If the query returns 0 instances, insert the row.
Here's some light code to present the idea (may need to be modified to work with your database):
SELECT COUNT(1) FROM MyJoinTable WHERE AttributeId = #RequestedID
IF ##ROWCOUNT = 0
BEGIN
INSERT INTO MyJoinTable ...
END
You can control your inserts via a stored procedure. My understanding is that
users can select a combination of Attributes, such as
just 1
1 and 10 together
1,4,5,10 (4 attributes)
These need to enter the table as a single "batch" against a (new?) productAttributeId
So if (1,10) was chosen, this needs to be blocked because 1-2 and 10-2 already exist.
What I suggest
The stored procedure should take the attributes as a single list, e.g. '1,2,3' (comma separated, no spaces, just integers)
You can then use a string splitting UDF or an inline XML trick (as shown below) to break it into rows of a derived table.
Test table
create table attrib (attributeid int, productattributeid int)
insert attrib select 1,1
insert attrib select 1,2
insert attrib select 10,2
Here I use a variable, but you can incorporate as a SP input param
declare #t nvarchar(max) set #t = '1,2,10'
select top(1)
t.productattributeid,
count(t.productattributeid) count_attrib,
count(*) over () count_input
from (select convert(xml,'<a>' + replace(#t,',','</a><a>') + '</a>') x) x
cross apply x.x.nodes('a') n(c)
cross apply (select n.c.value('.','int')) a(attributeid)
left join attrib t on t.attributeid = a.attributeid
group by t.productattributeid
order by countrows desc
Output
productattributeid count_attrib count_input
2 2 3
The 1st column gives you the productattributeid that has the most matches
The 2nd column gives you how many attributes were matched using the same productattributeid
The 3rd column is how many attributes exist in the input
If you compare the last 2 columns and the counts
match - you can use the productattributeid to attach to the product which has all these attributes
don't match - then you need to do an insert to create a new combination