SQL Server: Using CHARINDEX to parse multiplying problems - sql

I have a data column that stores products distribution units as 1*1 or 1*2*6. I want to formulate a computed column and have the result of the multiply problem.
below is illustrated example
sku du computed_du
12345678 1*2 2
12345679 1*3 3
12345680 1*6*2 12
Is there a most effective way to perform this calculation on sql server?

This is definitely something that should be calculated elsewhere, but I have seen the below technique used as a crude product aggregation.
I am not positive about using this for computed column, but here is how you might pull it off: Wrap this in a function, schema bind it to your table, and reference it the computed column definition (not sure if mssql will allow this due to deterministic requirement of udf).
Uses split function from here.
declare #tab table (
sku int,
du varchar(10),
computed_du int
)
insert into #tab
select 12345678, '1*2', null union all
select 12345679, '1*3', null union all
select 12345680, '1*6*2*0', null
--
select sku, du, min(s), case when min(cast(s as int)) = 0 then 0 else exp(sum(log(cast(nullif(s, 0) as int)))) end
from #tab
cross
apply dbo.Split('*', du)d
where cast(s as int)>0
group
by sku, du;

Related

SQL aggregate values and add new columns

I am having some trouble to aggregate data on row-inputs. I have two columns originally, but i want to split the data based on PortID and add five columns which now lie in the AssetClass column as row values.
The first table is how the data is structured now, the second is what i want it to look like.
Anyone have any tips how to do this? Thanks in advance.
You basically need to pivot your data set. Below query will pivot you data set purely using UNION and GROUP BY Clause which will be supported by any database system. You can add your pivot asset class S,U,P inside set_union block as done for C,F
Alternatively, you can simply use PIVOT operator.
CREATE TABLE sample
(
id int,
port_id nvarchar(10),
percent_weight int,
asset_class nvarchar(1)
);
INSERT INTO sample values
('1','SOL','15','C'),
('2','EID','20','C'),
('3','PAR','25','C'),
('45','SOL','30','F'),
('46','EID','40','F'),
('47','PAR','45','F')
;
SELECT
port_id,
MAX(C) AS C,
MAX(F) AS F
FROM
(
SELECT
id,
port_id,
percent_weight AS C,
NULL AS F
FROM
sample
WHERE
asset_class='C'
UNION
SELECT
id,
port_id,
NULL AS C,
percent_weight AS F
FROM
sample
WHERE
asset_class='F'
)set_union
GROUP BY
port_id
ORDER BY
id ASC;
Output:

Finding max value for a column containing hierarchical decimals

I have a table where the column values are like '1.2.4.5', '3.11.0.6',
'3.9.3.14','1.4.5.6.7', N/A, etc.. I want to find the max of that particular column. However when i use this query i am not getting the max value.
(SELECT max (CASE WHEN mycolumn = 'N/A'
THEN '-1000'
ELSE mycolumn
END )
FROM mytable
WHERE column like 'abc')
I am getting 3.9.3.14 as max value instead of 3.11....
Can someone help me?
Those aren't really decimals - they're strings containing multiple dots, so it's unhelpful to think of them as being "decimals".
We can accomplish your query with a bit of manipulation. There is a type build into SQL Server that more naturally represents this type of structure - hierarchyid. If we convert your values to this type then we can find the MAX fairly easily:
declare #t table (val varchar(93) not null)
insert into #t(val) values
('1.2.4.5'),
('3.11.0.6'),
('3.9.3.14'),
('1.4.5.6.7')
select MAX(CONVERT(hierarchyid,'/' + REPLACE(val,'.','/') + '/')).ToString()
from #t
Result:
/3/11/0/6/
I leave the exercise of fully converting this string representation back into the original form as an exercise for the reader. Alternatively, I'd suggest that you may want to start storing your data using this datatype anyway.
MAX() on values stored as text performs an alphabetic sort.
Use FIRST_VALUE and HIERARCHYID:
SELECT DISTINCT FIRST_VALUE(t.mycolumn) OVER(
ORDER BY CONVERT(HIERARCHYID, '/' + REPLACE(NULLIF(t.mycolumn,'N/A'), '.', '/') + '/') DESC) AS [Max]
FROM #mytable t

select max of mixed string/int column

I have a table who returns me a value as bellow
id_unique name serie timeB timeD
155488EA-FF70-49D7-99AB-AFD4125F3435 dell 14296188 05:51 06:19
1B640883-0DB6-4255-B1ED-770B6578064C dell 14295943 04:37 04:39
I want the max of the value i have tried a sql as bellow :
select max(cast(id_unique as varchar(36))),
max(name),max(serie),max(timeB),max(timeD) from mytable group by name
i got this result
1B640883-0DB6-4255-B1ED-770B6578064C dell 14296188 05:51 06:19
But the result that i need is this one :
155488EA-FF70-49D7-99AB-AFD4125F3435 dell 14296188 05:51 06:19
This any metho to fix that ?
My guess is that you want something like this using row_number:
select *
from (
select *, row_number() over (order by somevalue desc) rn
from yourtable
) t
where rn = 1
Where somevalue is the column you need the max of. I presume you are currently trying to use max for every field -- that would explain your output...
If these are really stored as strings, I'm guessing that you want to cast the first column as a uniqueidentifier before finding the max, otherwise they will be evaluated alphabetically.
SQL Server 2012 and above
You can directly get the min/max of uniqueidentifiers:
select max(cast(id_unique as uniqueidentifier))
from MyTable
Previous Versions
You should cast as binary(16) before finding the min/max. I'm then casting back to uniqueidentifier for readability of the results:
select cast(max(cast(cast(id_unique as uniqueidentifier) as binary(16))) as uniqueidentifier)
from MyTable

SQL - String Logic

I don't know if what I am wanting to achieve is possible so here is my conundrum.
Within a SQL table there are a number of fields that contain yes/no flags in a string so for example. On the field may be be called 'Stock' and within this one field there is a string of flags which e.g. 'YNYYY' lets say for example that the flags stand for.
Coke
Fanta
Pepsi
Lilt
Dr Pepper
in this instance I would want in my return of data to return Coke,Pepsi,Lilt,Dr Pepper ommiting the Fanta.
Now this would be possible using the CASE Statement and this may be the answer that I have to use, however ideally so I don't have to write hundreds of different variables anyone know of a way this could be achieved?
Your help as always appreciated, I've done the normal googling and maybe I simply don't know what to search for as its giving me blanks.
Please point me in the right direction.
Regards
R
Why dont you use SELECT with WHERE?
Something like this.
SELECT GROUP_CONCAT(`Stock`)
FROM table_name
WHERE `flag` = 'Y'
Hope this helps.
One way you can achieve your goal is by writing a table valued function that turns your Y/N string into a table of (Id INT, value BIT), you could then join to a look up table based on convention. Here's what something like this would look like:
CREATE FUNCTION udf_StringToBool(#intput varchar(100))
RETURNS #table TABLE (
Id INT IDENTITY(1,1),
Value BIT
)
AS
begin
declare #temp_input varchar(100) = #intput
while len(#temp_input) > 0
begin
insert into #table (value)
SELECT CASE LEFT(#temp_input, 1) WHEN 'Y' THEN 1 ELSE 0 END
set #temp_input = right(#temp_input, len(#temp_input)-1)
END
RETURN
end
You would then join your stock (and a lookup to product) table with this function, then remove any that are not in stock in the WHERE clause:
SELECT s.*, v.Value, pl.Name
FROM
stock s
cross apply
(
select b.* from udf_StringToBool(s.Flags) b
) v
join product_lookup pl on pl.Id = v.Id
WHERE v.Value = 1
Here's how you would define the lookup table:
create table product_lookup
(
Id INT IDENTITY(1,1),
Name Varchar(50)
)
insert into product_lookup (Name) values
('Coke'),
('Fanta'),
('Pepsi'),
('Lilt'),
('Dr Pepper')
Then you could use PIVOT to generate the columns with booleans.
In the end I chose to use 'substring' and the 'case' statement so that each item appeared in it's own field this way I mitigated the need to write every variable.
SELECT CASE WHEN SUBSTRING(STOCK,1,1) = 'y' THEN 'IN STOCK' ELSE 'OUTOFSTOCK' END AS COKE
I don't know why it didnt occur to me to begin with and without your prompting and guidance I would have probably done this the long way round as ever thanks to all!

How do you query an int column for any value?

How can you query a column for any value in that column? (ie. How do I build a dynamic where clause that can either filter the value, or not.)
I want to be able to query for either a specific value, or not. For instance, I might want the value to be 1, but I might want it to be any number.
Is there a way to use a wild card (like "*"), to match any value, so that it can be dynamically inserted where I want no filter?
For instance:
select int_col from table where int_col = 1 // Query for a specific value
select int_col from table where int_col = * // Query for any value
The reason why I do not want to use 2 separate SQL statements is because I am using this as a SQL Data Source, which can only have 1 select statement.
Sometimes I would query for actual value (like 1, 2...) so I can't not have a condition either.
I take it you want some dynamic behavior on your WHERE clause, without having to dynamically build your WHERE clause.
With a single parameter, you can use ISNULL (or COALESCE) like this:
SELECT * FROM Table WHERE ID = ISNULL(#id, ID)
which allows a NULL parameter to match all. Some prefer the longer but more explicit:
SELECT * FROM Table WHERE (#id IS NULL) OR (ID = #id)
A simple answer would be use: IS NOT NULL. But if you are asking for say 123* for numbers like 123456 or 1234 or 1237 then the you could convert it to a varchar and then test against using standard wild cards.
In your where clause: cast(myIntColumn as varchar(15)) like '123%'.
Assuming the value you're filtering on is a parameter in a stored procedure, or contained in a variable called #Value, you can do it like this:
select * from table where #Value is null or intCol = #Value
If #Value is null then the or part of the clause is ignored, so the query won't filter on intCol.
The equivalent of wildcards for numbers are the comparators.
So, if you wanted to find all positive integers:
select int_col from table where int_col > 0
any numbers between a hundred and a thousand:
select int_col from table where int_col BETWEEN 100 AND 1000
and so on.
I don't quite understand what you're asking. I think you should use two different queries for the different situations you have.
When you're not looking for a specific value:
SELECT * FROM table
When you are looking for a specific value:
SELECT * FROM table WHERE intcol = 1
You can use the parameter as a wildcard by assigning special meaning to NULL:
DECLARE #q INT = 1
SELECT * FROM table WHERE IntegerColumn = #q OR #q IS NULL
This way, when you pass in NULL; you get all rows.
If NULL is a valid value to query for, then you need to use two parameters.
If you really want the value of your column for all rows on the table you can simply use
select int_col
from table
If you want to know all the distinct values, but don't care how many times they're repeated you can use
select distinct int_col
from table
And if you want to know all the distinct values and how many times they each appear, use
select int_col, count(*)
from table
group by int_col
To have the values sorted properly you can add
order by int_col
to all the queries above.
Share and enjoy.