Updating a table column using LIKE in WHERE - sql

I have a table(ENTITY) that needs to be updated based on an ID(FUNNCODE) but the ID(FUNNCODE) is linked between two other tables(from JOINT then to POSITION)
and is independent of where the data is at(table NEORSD). The only parameter I can bind is the position name between the NEORSD table and POSITION table. When I place my LIKE statement into the where clause I get an error in return. If anyone can point me in the right direction it would be greatly appreciated!
Tables:
NEORSD: Contains the range information and 'position name(= Tag_No)'
ENTITY: Needs to update and accept the range information (Holds FUNCCODE)
JOINT: Holds FUNCCODE(named POSFUNCCODE) and corresponding POSCODE
POSITION: Contains POSCODE and 'position name(=POSID)'
UPDATE ENTITY
SET
RANGE0 = (
SELECT RANGE0
FROM NEORSD_1199
WHERE Tag_No like ('%PIT%'))
WHERE
FUNCCODE = (
SELECT POSFUNCCODE
FROM JOINT
WHERE POSCODE = (
SELECT POSCODE
FROM POSITION
WHERE POSID like ('%PIT%'))

If NEORSD_1199 has more than one row with a tag_no like '%PIT%', which NEORSD_1199.RANGE0 value should it use to update ENTITY.RANGE0?
This is the db engine's problem with your SQL.
To better understand, read the SQL backwards:
First you're getting a list of every Position Code from the POSITION table where the Position ID is like '%PIT%'. That might be one code, and it might be one hundred codes.
Then you're getting every Position Function Code from the JOINT table where the Position Code is in the list of Position Codes you just gathered. Again, could be one, could be a hundred.
Then you're getting a list of all values of RANGE0 from the NEORSD1199 table where Tag_No is like '%PIT%'. Again, this could be one value, or a list of one hundred.
Then, you're getting every row from the ENTITY table where the Function Code is in the list of Position Function Codes you gathered from the JOINT table (step 2 above), and you're updating RANGE0 in each of these rows to the value you captured in step 3.
The problem is that the 'value' returned in step 3 could be a list of values. If
NEORSD1199 has four rows where tag number is like '%PIT%'
(e.g. PIT01,PIT02,PIT03,APIT00), and each of those rows has a different
RANGE0 (e.g. 1,2,3,99), then which of those four values should the DB engine use to update RANGE0 in the rows in the ENTITY table?

Thank you to #SQLCliff for the questions that help to find the solution. I created an ID column inside my NEORSD table, created a temporary table holding the link between FUNCCODE and the ranges in NEORSD. Then I updated ENTITY using a join on. I can insert the where clause at the end of the temporary table for filtering if needed. Since it is a mass update I no longer require a where clause. My brain just likes making things more complicated than they need to be XD
with t as(
select f.funccode as funccode ,n.range0, n.range100
from func as f
join NEORSD_1199_With_Ranges_Updated as n on n.id = f.poscode or n.id =f.devcode
/* WHERE nessecrary ;P*/)
update entity
set
range0 = t.range0,
range100 = t.range100
from entity as e
join t on e.funccode = t.funccode

Related

PostGIS st_point functoin returns null values for all records that do not meet criteria

I am trying to create geom values for new records coming from field devices that have latitude and longitude value in two fields (latitude, longitude), for a given project using the following expression:
UPDATE public.data_mds_import
SET geom=(
SELECT
st_force3d(st_setsrid(st_point(public.data_mds_import.longitude, public.data_mds_import.latitude),4326))
WHERE PUBLIC.data_mds_import.id_jobcode=220 /* ENTER ID_JOBCODE HERE */
);
We have lots of field data coming in and i need a statement where i can just change the jobcode value in the statement for each new set of field data that gets imported.
The problem is that for other jobs in the table where jobcode != 220, the function returns null values, i.e. it removes the geom value, i lose my geometry data so i go from having several thousand records with a geometry value before the function runs, to only the records where jobcode = '220'.
Why is this happening, the statement should be selecting only records where jobcode=220 right? Whereas it appears to select all records and return the geometry where jobcode = 220, and null for every other record. No action would be better than null.
Should i use a case statement? If so can someone please help with such a statement, i have tried several case statement variations and cant get a statement to execute.
Welcome to SO.
If you're updating a single table, there is no need to use a suquery to simply update a column. Also, you don't need to repeat the schema and table names.
This query selects all records that correspond to id_jobcode = 220 and creates a geometry in the column geom from the values of latitude and logitude:
UPDATE public.data_mds_import
SET geom = ST_Force3D(
ST_SetSRID(
ST_Point(longitude, latitude),4326))
WHERE id_jobcode = 220;

Modifying a SQL table to insert data from an existing row into the previous row

I have a SQL table called RawData with the following six columns (all type VARCHAR):
Name StreetAddress State Phone Website Email
I import new data from a CSV file into this table on a regular basis and 99% of it is formatted correctly. For 1% of the data, for reasons that I understand but are not important for this question, the data is imported such that all of the first five columns (Name, StreetAddress, State, Phone, Website) have data that is formatted correctly but the sixth column Email is blank. Furthermore, the email is imported into its own row immediately below this "problem" row but the email is placed in the Name column. These situations are readily identified begins the email address is always inserted in the Name column with the mailto: prefix .
What I would like to do is devise a query to take the incorrectly placed email address from the Name, strip the mailto: prefix and then place it into the Email column in the previous row. I would then like to delete the row with the 'mailto:' email in the `Name' column.
I've done a bit of research and believe that I need to use a cursor to copy the data from the row below the correct row and insert it into the Email column but having never used cursors before, I am struggling to come up with anything. Any help would be appreciated.
Edit: For the purposes of brevity, I omitted that the table does contain an identity column ID.
In SQL Server, there is no concept of "previous" row. You are going to need to add an identity column to the table so you can identify these situations. Fortunately, you can insert your data into a view, so the identity is automatically incremented in the load order.
Once you have the id, you can use an update:
update nd
set nd.email = stuff(ndnext.name, 1, 7, '')
from newdata nd join
newdata ndnext
on nd.id = ndnext.id - 1
where ndnext.name like 'mailto:%';
Once you have verified that this is correct, you probably want to do:
delete nd from nd
where nd.name like 'mailto:%';

SQL: I need to take two fields I get as a result of a SELECT COUNT statement and populate a temp table with them

So I have a table which has a bunch of information and a bunch of records. But there will be one field in particular I care about, in this case #BegAttField# where only a subset of records have it populated. Many of them have the same value as one another as well.
What I need to do is get a count (minus 1) of all duplicates, then populate the first record in the bunch with that count value in a new field. I have another field I call BegProd that will match #BegAttField# for each "first" record.
I'm just stuck as to how to make this happen. I may have been on the right path, but who knows. The SELECT statement gets me two fields and as many records as their are unique #BegAttField#'s. But once I have them, I haven't been able to work with them.
Here's my whole set of code, trying to use a temporary table and SELECT INTO to try and populate it. (Note: the fields with # around the names are variables for this 3rd party app)
CREATE TABLE #temp (AttCount int, BegProd varchar(255))
SELECT COUNT(d.[#BegAttField#])-1 AS AttCount, d.[#BegAttField#] AS BegProd
INTO [#temp] FROM [Document] d
WHERE d.[#BegAttField#] IS NOT NULL GROUP BY [#BegAttField#]
UPDATE [Document] d SET d.[#NumAttach#] =
SELECT t.[AttCount] FROM [#temp] t INNER JOIN [Document] d1
WHERE t.[BegProd] = d1.[#BegAttField#]
DROP TABLE #temp
Unfortunately I'm running this script through a 3rd party database application that uses SQL as its back-end. So the errors I get are simply: "There is already an object named '#temp' in the database. Incorrect syntax near the keyword 'WHERE'. "
Comment out the CREATE TABLE statement. The SELECT INTO creates that #temp table.

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

How to update column with the data from the columns in the same row in MYSQL?

I have a table house with fields price and area, now I want to add one more column named 'average_price'.
How could I set the average_price according to the price and area in MYSQL?
Just one idea:
Add your column in your favorite editor/method
Loop through your table and define average_price value
Update actual row with your value
UPDATE your_table SET average_price = price/area;
You can do it with a suquery. But I don't know about performance.
UPDATE `houses` AS h
SET `avg_price` = (
SELECT AVG(`price`)
FROM `houses` AS h2
WHERE h2.area == h.area
)
Or you could create a temporary table in which to put the average prices and then update.
This doesn't sound like the kind of data you should actually store, by how often you'd have to update it - once every single time you modify the house table in any way! That's a lot of unnecessary load on the database, and a whole lot of room for human error.
Instead, I recommend acquiring it on the fly through a SQL query when you need it, and never actually storing it.
Retrieving from all areas:
SELECT `area`, AVG(`price`) AS `average_price`
FROM `house`
GROUP BY `area`
ORDER BY `average_price` ASC
Retrieving from a specific area:
SELECT `area`, AVG(`price`) AS `average_price`
FROM `house`
WHERE `area` = "New York"
GROUP BY `area`