Issue in Selection with CHARINDEX and SUBSTRING - sql

I have 2 questions here.
I have a column 'Campaign' with these entries. My objective is to extract the Cost part from the string and create a new column Cost in my table
29693214 - Live -JUTL Phase 2 Creator Stories Trailer * 7.12 - 7.25 * Video Views * $28,169.01 * BG - Mob
89695072 - Live -WUTL Retargeting JG * 7.16 - 7.31 * Link Clicks * $23,474.18 * KG - Mob
I tried select SUBSTRING(Campaign,CHARINDEX('$',Campaign) +1,???) .I am unable to figure out the '???' part. I want to start with the index next to '$' and continue till the '*' symbol to capture the cost.
While creating a new column I follow these steps
Alter table T ADD NewColumn varchar(100)
then I do this
Update T SET NewColumn = 'Say I want that cost part from the above question here'
Is there any efficient way to do this in single shot?

I think apply makes these operations a bit simpler:
select left(v1.str1, charindex(' ', v1.str1))
from (values ('29693214 - Live -JUTL Phase 2 Creator Stories Trailer * 7.12 - 7.25 * Video Views * $28,169.01 * BG - Mob')) v(str) cross apply
(values (stuff(str, 1, charindex('$', str), ''))) v1(str1);
This can readily be incorporated into an update:
Update t
set newcolumn = left(v.str1, charindex(' ', v.str1))
from t cross apply
(values (stuff(t.str, 1, charindex('$', str), ''))) v(str1)

DDL for testing query:
declare #tbl table(Campaign varchar(200));
insert into #tbl values
('29693214 - Live -JUTL Phase 2 Creator Stories Trailer * 7.12 - 7.25 * Video Views * $28,169.01 * BG - Mob'),
('189695072 - Live -WUTL Retargeting JG * 7.16 - 7.31 * Link Clicks * $23,474.18 * KG - Mob');
If the cost is always separated by a asterisk * from other part of the string, you could use below query:
select trim(substring(Campaign, dollarIndex + 1, asteriskIndex - dollarIndex - 1)) from (
select Campaign, charindex('$', Campaign) dollarIndex,
charindex('*', Campaign, charindex('$', Campaign)) asteriskIndex
from #tbl
) a
To use it in update statement, you could use the same query, but we need to transform above into single query:
update #tbl set Campaign = trim(substring(Campaign, charindex('$', Campaign) + 1, charindex('*', Campaign, charindex('$', Campaign)) - charindex('$', Campaign) - 1))
Note: consider if it's worthy keeping data that you already have (redundancy). If it can be always be parsed from your column in the same way you could use view for this task.
On the other hand it might be qiute expensive to query such data and might be bad for performance.

Related

postgres access a cte inside a view

is it possible, in postgresql - to access a cte defined in a view?
By that I mean - if you have the following:
create view my_view as
with
blah as (select 1 as x, 2 as y, 3 as z)
select
x*x as x_squared,
y*y as y_squared,
z*z as z_squared
from
blah
is there any way from outside of getting at blah? eg looking for something like:
select * from my_view.blah
Basically we have large views that use a number of complicated CTE's - and it's quite difficult sometimes to troubleshoot them without splitting them all out into separate smaller views [yes, I would prefer to just keep it like that, but I don't have that option]
I know I will be able to do it by making a stored proc that pulls out the view definition - extracts the with clauses, parses up to the blah definition, changes that to the main select, gets rid of the rest, and then does the query - but that all seems like a lot of work. Am hoping there's a built-in way?
You can create the CTE as a view by itself. For example:
create table a (b int);
insert into a (b) values (1), (50), (200), (350), (1000);
create view blah as
select * from a where b > 100;
Anf then base your original view on this new intermediate one to avoid repeating code:
create view my_view as
select * from blah where b < 500;
See running example at DB Fiddle.
ok - so I have a sort of solution - it's a not a function - it's a procedure that turns a cte into a materialized_view.
I first wanted to formulate it as function, so you could say:
select * from cte_from( 'my_real_view', 'the_cte' )
but it appears that a function needs its schema defined in advance, which is obviously impossible in this case. If anyone can suggest a hack to make it closer to above, I'd apperciate it. But anyway - bottom line this works:
create procedure from_cte(view_schema text, view_name text, cte_name text, materialized_view_name text) as
$func$
declare
_code text;
_others text;
_script text;
begin
execute format('drop materialized view if exists %s', materialized_view_name);
with recursive
string_provider as (
select view_definition as the_string,
position(concat(cte_name, ' as (') in lower(view_definition)) + length(cte_name) + 5 as start_location
from information_schema.views
where table_name = view_name
and table_schema = view_schema
),
string_walk as (
select start_location as x,
1 as brackets
from string_provider
union
select x + 1,
new_brackets
from string_provider,
string_walk,
lateral (select case
when substring(the_string from x + 1 for 1) = '(' then brackets + 1
when substring(the_string from x + 1 for 1) = ')' then brackets - 1
else brackets
end as new_brackets
) calculated
where new_brackets != 0
and x < length(the_string)
)
select substring(the_string from start_location for 1 + max(x) - start_location),
trim(substring(the_string from 0 for start_location - length(cte_name) - 5))
into _code, _others
from string_walk,
string_provider
group by the_string, start_location;
if length(_others) < 5 then
select _code into _script;
else
select concat(substring(_others from 0 for length(_others)), ' ', _script) into _script;
end if;
execute format('create materialized view %s as ( %s )', materialized_view_name, _code);
end
$func$ language plpgsql;

In memory queries using SQL Server

I'm afraid that even though I am using CTE's in my query, that maybe, behind the scenes, a lot of disk caching is going on -- so it may as well not be using CTE's.
The whole point of using CTE's was that my original query code was way too slow, and would eventually get a transport level error and crash.
Well, it's still too slow. Maybe even slower. I don't know yet.
Is there a way to tell SQL Server to go ahead and be resource hog for my query?
I am only guessing, but I think it is using disk space to cache memory results. When I look at task manager memory utilization, I see SSMS at 161 MB. SSMS is where I am running the query from.
Here is my code - you don't have to read it in detail, but in brief, the source table contains about a million rows.
I need a solution, so alternative ideas are welcome...
WITH MetEdFliers AS
(
SELECT DISTINCT
[CustomerName1], [Mailing_Address], [Mailing_Address2], [Mailing_Zip]
FROM
[dbo].[_MetEd_Detail]
WHERE
RunId = (SELECT RunId FROM LastLoadRuns WHERE UtilityId = 9)
AND [Profitable] = 1 -- and not low income, should flag exist
),
MetEdLookUpFirst AS
(
-- same as [dbo].[VW_MetEd_Master_Profitable_ExcludeBadAddress]
SELECT
IIF (DET.IncalculableMailAddress = 1,
IIF (AA.Address1 IS NULL, 'Bad Address Undefined Fix -- Source Address Provided', 'Fixed Bad Source Address Via Lookup'), '') AS AddressStatus,
DET.ACCT_NO,
(CAST(DET.Monthly1 as Decimal) +
CAST(DET.Monthly2 as Decimal) +
CAST(DET.Monthly3 as Decimal) +
CAST(DET.Monthly4 as Decimal) +
CAST(DET.Monthly5 as Decimal) +
CAST(DET.Monthly6 as Decimal) +
CAST(DET.Monthly7 as Decimal) +
CAST(DET.Monthly8 as Decimal) +
CAST(DET.Monthly9 as Decimal) +
CAST(DET.Monthly10 as Decimal) +
CAST(DET.Monthly11 as Decimal) +
CAST(DET.Monthly12 as Decimal)) AS BilledKWHTotal,
DET.Polar, DET.CustomerName1,
REPLACE (IIF (DET.IncalculableMailAddress = 1,
IIF (AA.Address1 IS NULL, DET.Mailing_Address, AA.Address1), DET.Mailing_Address), ',', ';') AS Address1,
REPLACE (IIF (DET.IncalculableMailAddress = 1,
IIF (AA.Address2 IS NULL, DET.Mailing_Address2, AA.Address2), DET.Mailing_Address2), ',', ';') AS Address2,
REPLACE (IIF (DET.IncalculableMailAddress = 1,
IIF (AA.City IS NULL, DET.Mailing_City, AA.City), DET.Mailing_City), ',', ';') AS City,
IIF (DET.IncalculableMailAddress = 1,
IIF (AA.[State] IS NULL, DET.Mailing_State, AA.[State]), DET.Mailing_State) AS [State],
IIF (DET.IncalculableMailAddress = 1,
IIF (AA.Zip IS NULL, DET.Mailing_Zip, AA.Zip), DET.Mailing_Zip) AS ZIP,
IIF (DET.IncalculableMailAddress = 1, '', DET.Mailing_Zip4) AS ZIP4,
REPLACE (DET.Address, ',', ';') AS ServiceAddress,
REPLACE (DET.City, ',', ';') AS ServiceAddressCity,
DET.State ASs ServiceAddressState,
DET.Zip AS ServiceAddressZip,
DET.Zip4 AS ServiceAddressZip4,
DET.ProfitAnnualPotential AS [Potential Annual Profit]
FROM
_MetEd_DETAIL DET
LEFT JOIN
AccountAddress AA ON (DET.ACCT_NO = AA.ACCT_NO AND AA.UtilityId = 9)
WHERE
RunId = (SELECT RunId FROM LastLoadRuns WHERE UtilityId = 9)
AND DET.Profitable = 1 --AND det.CAP_CUSTOMER = 0
AND (DET.IncalculableMailAddress = 0 OR (AA.Address1 IS NOT NULL))
)
SELECT X.*
FROM MetEdFliers Fliers
OUTER APPLY
(SELECT TOP 1 *
FROM MetEdLookUpFirst LU
WHERE LU.CustomerName1 = Fliers.CustomerName1
AND LU.Address1 = Fliers.Mailing_Address
AND LU.Address2 = Fliers.Mailing_Address2
AND LU.Zip = Fliers.Mailing_Zip) X
It looks to be difficult.
I am going to handle it in my source program that generates input files for the sql server database ( i real the cvs into a table using import ).
To handle this problem, I am going to use a technology called dictionary, with key value pair, in c#.
I will be able to tell if the key had been added before, and if so, I replace the key value pair with the new key value pair and the annual profit potential field with the sum from both records....
Note: Prior method, I did not have sum (enhancement).

Long arithmetic with SQL SELECT statement

The basic idea is I have 3 separate SELECT statements that output a number. I need a percentage from those numbers. So the problem would look like this:
(a.count_one + b.count_two) -
(b.count_two + c.count_three) / (a.count_one + b.Count_two) * 100
When I do the code found below I only get the output of the first (a.count_one + b.count_two). BUT if I comment out the third portion which is / (a.count_one + b.count_two) I successfully get the correct solution from (a.count_one + b.count_two) – (b.count_two + c.count_three).
So it appears, doing math of two statements is great, but when I throw in a third statement it pukes and only wants to show the solution of the first problem (a.count_one + b.count_two), but does not even try to calculate the solution for (a.count_one + b.count_two) – (b.count_two + c.count_three) anymore. I'm a bit stumped why.
Select
(a.Count_one + b.Count_two) -
(b.Count_two + c.Count_three) / (a.Count_one + b.Count_two) * 100
as 'Final_Percentage'
from
(
select COUNT(v_Summary.ResourceID) AS Count_one
From [DB1].[dbo].[v_Summary]
) a,
(
select count([CN]) as Count_two
From [DB2].[dbo].[Computers]
WHERE cn NOT IN (SELECT name0 FROM [DB1].[dbo].[v_system] where v_system.Client0 = '1')
) b,
(
select COUNT(v_Summary.ResourceID) AS Count_three
From [DB1].[dbo].[v_Summary]
Where Description like '%/Fail'
) c
And to give additional information. The math problem with numbers:
(54558 + 373) – (373 + 117) / (54558 + 373) * 100
Or further solved:
(54931) - (490) / 55304 * 100 = 98.44%
COUNT function returns int. When you divide two int values the result is again int in SQL Server. So, 490 / 55304 is 0. Then 0 * 100 is 0 again.
Cast the values to a suitable type, like float.
By the way,
(54931) - (490) / 55304 * 100
is not equal to ~98.44, it is equal to ~54930.11399
You need parentheses to get the result you expect:
((54931) - (490)) / 55304 * 100
is equal to ~98.43953421
So, the final query should look like this:
Select
((a.Count_one + b.Count_two) - (b.Count_two + c.Count_three))
/ (a.Count_one + b.Count_two) * 100
as 'Final_Percentage'
from
(
select CAST(COUNT(v_Summary.ResourceID) AS float) AS Count_one
From [DB1].[dbo].[v_Summary]
) a,
(
select CAST(count([CN]) AS float) as Count_two
From [DB2].[dbo].[Computers]
WHERE cn NOT IN (SELECT name0 FROM [DB1].[dbo].[v_system] where v_system.Client0 = '1')
) b,
(
select CAST(COUNT(v_Summary.ResourceID) AS float) AS Count_three
From [DB1].[dbo].[v_Summary]
Where Description like '%/Fail'
) c
You should also get the right result by saying
Select
100.*(a.Count_one - c.Count_three) / (a.Count_one + b.Count_two)
as 'Final_Percentage'
from ... -- unchanged subqueries, as supplied in your question
By moving the *100. right to the front you implicitly convert the first product to float and the type then stays that way for the rest of the calculation. Also, you might have noticed, I simplified the formula, since b.Count_two gets subtracted from itself, so you can leave it out altogether. I have not tested this but I am pretty sure it will work.

SQL update only values whose value is not already updated

I have a Query it updates the whole tickets in table.
I want it to update only the tickets whose values needs to be updated not update all rows.
e.g.
If slabreachdays is already 10 then new value is also 10 it should not update.
This is my update query.
update ticket
set TICKET.slabreachdays =
FLOOR(((DAYS(TICKET.creationdate) - DAYS(current timestamp)+10)
* 86400 + (MIDNIGHT_SECONDS(TICKET.creationdate) -
MIDNIGHT_SECONDS(current timestamp)))/86400.0)
where TICKET.VENDOR like 'ABC'
and TICKET.STATUS NOT IN('CANCELLED','CLOSED')
This is my select query which selects only the tickets that needs to be updated. This is the query I need to convert to an update query
select * from (
select ticketid,slabreachdays,
FLOOR(((DAYS(TICKET.creationdate) - DAYS(current timestamp)+10) * 86400 + (MIDNIGHT_SECONDS(TICKET.creationdate) - MIDNIGHT_SECONDS(current timestamp)))/86400.0)
as newValue
from ticket
where TICKET.MLOUTSOURCEVENDOR like 'ABC' and TICKET.STATUS NOT IN('CANCELLED','CLOSED'))
where SLABREACHDAYS != newValue
Try it
Where .... And slabeachdays<> aller calculated expression
Try this
update ticket
set TICKET.slabreachdays =
FLOOR(((DAYS(TICKET.creationdate) - DAYS(current timestamp)+10)
* 86400 + (MIDNIGHT_SECONDS(TICKET.creationdate) -
MIDNIGHT_SECONDS(current timestamp)))/86400.0)
where TICKET.VENDOR like 'ABC'
and TICKET.STATUS NOT IN('CANCELLED','CLOSED') and
TICKET.slabreachdays <>
(FLOOR(((DAYS(TICKET.creationdate) - DAYS(current timestamp)+10)
* 86400 + (MIDNIGHT_SECONDS(TICKET.creationdate) -
MIDNIGHT_SECONDS(current timestamp)))/86400.0))

Add a random number between 30 and 300 to an existing field

I have looked on the net as well as here but can't find an answer to the following MySQL question. I'm looking to replace the value of an existing field with a query that has a random number between 30 and 300.
Reason was because I've moved galleries and had 250,000,000 views on my images and there have been lost with the migration and a lot of my members are upset that they have lost views....
UPDATE the_table SET the_field = the_field + FLOOR(RAND() * (270 + 1)) + 30
Use RAND()
UPDATE table
SET field = FLOOR(30 + (RAND() * 270));
WHERE foo = 'bar'
I think this will do the trick:
UPDATE table SET field = ROUND(30 + (RAND() * 270)) WHERE id =1;