Parse values from one column into multiple columns in another table - sql

Microsoft SQL Server 2008
I have two tables. One has a column in it with data that is _ delimited
example:
Y_21_CA
<BR>
such that:
Yes/No_Age_State
I need to parse the data in this column out and insert it into another table that has individual columns for each value. Let's say Table A contains one column like the example above but Table B contains three columns, YesOrNo, Age, usState.
What's the easiest way to do this? I've tried doing something like:
UPDATE TableA
SET YesOrNo = SUBSTRING (TableB.Column1, 1, 1)
but SUBSTRING only takes an expression. I really just need some guidance here, I've been banging my head against the wall trying to figure this out since I'm not much of a SQL guru. I can figure out the syntax no problem but maybe I'm not aware of some methods that exist. Thanks

A generic solution, using Charindex of '_' without hardcoding it
declare #s varchar(10) = 'Y_21_CA'
SELECT LEFT(#s, CHARINDEX('_',#s,1)-1) YN,
SUBSTRING(#s, CHARINDEX('_',#s,1)+1,
CHARINDEX('_',#s,CHARINDEX('_',#s,1)) ) Age,
RIGHT(#s, CHARINDEX('_',reverse(#s),1)-1) State
--Results
Y 21 CA
In case you expect to use this logic often in other queries, you could make the SELECT statement an inline TVF. Then you would be able to use it with your update like this:
UPDATE b
SET YesOrNo = x.YN,
Age = x.Age,
State = x.State
FROM TableB b
INNER JOIN TableA a ON b.ID = a.ID
CROSS APPLY ThreeColumnSplit(a.S) x;
Here's a "live" demo at SQL Fiddle. (Please never mind its using the SQL Server 2012 engine. That's only because the Fiddle's 2008 instance appears to be down at the moment and can't be used. There's nothing SQL Server 2012-specific in the script.)

You should be able to use the following, I think you want an INSERT instead of UPDATE.
The SELECT statement to get the data is:
select substring(yourCol, 1, 1) YesOrNo,
substring(yourcol, 3, len(yourcol)-5) Age,
right(yourCol, 2) usState
from tableA;
See SQL Fiddle with Demo
Then the INSERT statement is:
insert into tableB (YesOrNo, Age, usState)
select substring(yourCol, 1, 1) YesOrNo,
substring(yourcol, 3, len(yourcol)-5) Age,
right(yourCol, 2) usState
from tableA;
See SQL Fiddle with Demo
Note: This assumes that the YesOrNo column will always only have one character and that the usState will always have 2 characters.

Related

'In' clause in SQL server with multiple columns

I have a component that retrieves data from database based on the keys provided.
However I want my java application to get all the data for all keys in a single database hit to fasten up things.
I can use 'in' clause when I have only one key.
While working on more than one key I can use below query in oracle
SELECT * FROM <table_name>
where (value_type,CODE1) IN (('I','COMM'),('I','CORE'));
which is similar to writing
SELECT * FROM <table_name>
where value_type = 1 and CODE1 = 'COMM'
and
SELECT * FROM <table_name>
where value_type = 1 and CODE1 = 'CORE'
together
However, this concept of using 'in' clause as above is giving below error in 'SQL server'
ERROR:An expression of non-boolean type specified in a context where a condition is expected, near ','.
Please let know if their is any way to achieve the same in SQL server.
This syntax doesn't exist in SQL Server. Use a combination of And and Or.
SELECT *
FROM <table_name>
WHERE
(value_type = 1 and CODE1 = 'COMM')
OR (value_type = 1 and CODE1 = 'CORE')
(In this case, you could make it shorter, because value_type is compared to the same value in both combinations. I just wanted to show the pattern that works like IN in oracle with multiple fields.)
When using IN with a subquery, you need to rephrase it like this:
Oracle:
SELECT *
FROM foo
WHERE
(value_type, CODE1) IN (
SELECT type, code
FROM bar
WHERE <some conditions>)
SQL Server:
SELECT *
FROM foo
WHERE
EXISTS (
SELECT *
FROM bar
WHERE <some conditions>
AND foo.type_code = bar.type
AND foo.CODE1 = bar.code)
There are other ways to do it, depending on the case, like inner joins and the like.
If you have under 1000 tuples you want to check against and you're using SQL Server 2008+, you can use a table values constructor, and perform a join against it. You can only specify up to 1000 rows in a table values constructor, hence the 1000 tuple limitation. Here's how it would look in your situation:
SELECT <table_name>.* FROM <table_name>
JOIN ( VALUES
('I', 'COMM'),
('I', 'CORE')
) AS MyTable(a, b) ON a = value_type AND b = CODE1;
This is only a good idea if your list of values is going to be unique, otherwise you'll get duplicate values. I'm not sure how the performance of this compares to using many ANDs and ORs, but the SQL query is at least much cleaner to look at, in my opinion.
You can also write this to use EXIST instead of JOIN. That may have different performance characteristics and it will avoid the problem of producing duplicate results if your values aren't unique. It may be worth trying both EXIST and JOIN on your use case to see what's a better fit. Here's how EXIST would look,
SELECT * FROM <table_name>
WHERE EXISTS (
SELECT 1
FROM (
VALUES
('I', 'COMM'),
('I', 'CORE')
) AS MyTable(a, b)
WHERE a = value_type AND b = CODE1
);
In conclusion, I think the best choice is to create a temporary table and query against that. But sometimes that's not possible, e.g. your user lacks the permission to create temporary tables, and then using a table values constructor may be your best choice. Use EXIST or JOIN, depending on which gives you better performance on your database.
Normally you can not do it, but can use the following technique.
SELECT * FROM <table_name>
where (value_type+'/'+CODE1) IN (('I'+'/'+'COMM'),('I'+'/'+'CORE'));
A better solution is to avoid hardcoding your values and put then in a temporary or persistent table:
CREATE TABLE #t (ValueType VARCHAR(16), Code VARCHAR(16))
INSERT INTO #t VALUES ('I','COMM'),('I','CORE')
SELECT DT. *
FROM <table_name> DT
JOIN #t T ON T.ValueType = DT.ValueType AND T.Code = DT.Code
Thus, you avoid storing data in your code (persistent table version) and allow to easily modify the filters (without changing the code).
I think you can try this, combine and and or at the same time.
SELECT
*
FROM
<table_name>
WHERE
value_type = 1
AND (CODE1 = 'COMM' OR CODE1 = 'CORE')
What you can do is 'join' the columns as a string, and pass your values also combined as strings.
where (cast(column1 as text) ||','|| cast(column2 as text)) in (?1)
The other way is to do multiple ands and ors.
I had a similar problem in MS SQL, but a little different. Maybe it will help somebody in futere, in my case i found this solution (not full code, just example):
SELECT Table1.Campaign
,Table1.Coupon
FROM [CRM].[dbo].[Coupons] AS Table1
INNER JOIN [CRM].[dbo].[Coupons] AS Table2 ON Table1.Campaign = Table2.Campaign AND Table1.Coupon = Table2.Coupon
WHERE Table1.Coupon IN ('0000000001', '0000000002') AND Table2.Campaign IN ('XXX000000001', 'XYX000000001')
Of cource on Coupon and Campaign in table i have index for fast search.
Compute it in MS Sql
SELECT * FROM <table_name>
where value_type + '|' + CODE1 IN ('I|COMM', 'I|CORE');

With as in Oracle SQL

I would like to know if is it possible to use the clause "with as" with a variable and/or in a block begin/end.
My code is
WITH EDGE_TMP
AS
(select edge.node_beg_id,edge.node_end_id,prg_massif.longueur,prg_massif.lgvideoupartage,prg_massif.lgsanscable from prg_massif
INNER JOIN edge on prg_massif.asset_id=edge.asset_id
where prg_massif.lgvideoupartage LIKE '1' OR prg_massif.lgsanscable LIKE '1')
,
journey (TO_TOWN, STEPS,DISTANCE,WAY)
AS
(SELECT DISTINCT node_beg_id, 0, 0, CAST(&&node_begin AS VARCHAR2(2000))
FROM EDGE_TMP
WHERE node_beg_id = &&node_begin
UNION ALL
SELECT node_end_id, journey.STEPS + 1
, journey.DISTANCE + EDGE_TMP.longueur,
CONCAT(CONCAT(journey.WAY,';'), EDGE_TMP.node_end_id
)
It create a string as output separated by a ; but i need to get it back as variable or table do you know how? I used a concat to retrieve data in a big string. Can i use a table to insert data
,
A need to use the result to proceed more treatment.
Thank you,
mat
No, WITH is a part of an SQL statement only. But if you describe why you need it in pl/sql, we'll can advice you something.
Edit: if you have SQL statement which produces result you need, you can assign it's value to pl/sql variable. There are several methods to do this, simpliest is to use SELECT INTO statement (add INTO variable clause into your select).
You can use WITH clause as a part of SELECT INTO statement (at least in not-too-very-old Oracle versions).

TSQL - ISNULL over multiple columns

I have a simple SQL query (SQL Server 2005) where I'm selecting from a table that contains multiple columns that have BIT values.
These columns are nullable so can contain NULL, 0 or 1.
There are a fair number of these columns and in my query I want to return zero if the value is NULL.
I'm currently using ISNULL like so:
SELECT Name, Age, ISNULL(LikesOranges,0), ISNULL(LikesApples,0), ISNULL(LikesPears,0)
FROM FoodPreferences
As I've mentioned, there are a lot of these BIT columns (much more than in the simple example above).
Is there a way I can use ISNULL over multiple columns like this:
SELECT ISNULL(*,0) FROM FoodPreferences
The above query doesn't work but you get what I'm trying to do - so I can avoid having to write an ISNULL statement for each column,
Thanks.
Try this:
SELECT COALESCE(LikesOranges, LikesApples, LikesPears) AS MyBit FROM FoodPreferences
This will return the first non-null value. If all fields are NULL the result is NULL.
UPDATE:
And the conclusion is:
SELECT ISNULL(COALESCE(LikesOranges, LikesApples, LikesPears),0) AS MyBit FROM FoodPreferences
so I can avoid having to write an
ISNULL statement for each column,
Run this query and copy the result to your select statement. system_type_id = 104 filters the result on bit columns.
select stuff((select ', isnull('+name+', 0)'
from sys.columns
where object_id = object_id('FoodPreferences') and
system_type_id = 104
for xml path('')), 1, 1, '')
Result:
-------------------------------------------------------------------------
isnull(LikesOranges, 0), isnull(LikesApples, 0), isnull(LikesPears, 0)
I don't think so. But an option might be to create a view onto that table and put all the ISNULL statements in the view. At least then you won't have to do it every time
eg.
CREATE VIEW vwFoodPreferences
AS
SELECT Name,
Age,
ISNULL(LikesOranges,0) AS LikesOranges,
ISNULL(LikesApples,0) AS LikesApples,
ISNULL(LikesPears,0) AS LikesPears
FROM FoodPreferences
Unfortunately, the simple answer is no.
You could write sql dynamically, but whatever happens, the final resulting sql would have to be ISNULL(a,0), ISNULL(b,0), ISNULL(c,0), ISNULL(d,0), etc
i think you can write a simple program and generate select clause by reading all columns and generating the select
while not this :
SELECT COALESCE(LikesOranges, LikesApples, LikesPears, 0) AS MyBit FROM FoodPreferences
https://learn.microsoft.com/en-us/sql/t-sql/language-elements/coalesce-transact-sql?view=sql-server-2017

SQL Server query brings unmatched data with BETWEEN filter

I'm querying on my products table for all products with code between a range of codes, and the result brings a row that should't be there.
This is my SQL query:
select prdcod
from products
where prdcod between 'F-DH1' and 'F-FMS'
order by prdcod
and the results of this query are:
F-DH1
F-DH2
F-DH3
FET-RAZ <-- What is this value doing here!?
F-FMC
F-FML
F-FMS
How can this odd value make it's way into the query results?
PS: I get the same results if I use <= and >= instead of between.
According to OP request promoted next comment to answer:
Seems like your collation excludes '-' sign - this way results make sense, FE is between FD and FM.
:)
between and >= and <= are primarily used for numeric operations (including dates). You're trying to use this for strings, which are difficult at best to determine how those operators will interpret the each string.
Now, while I think I understand your goal here, I'm not entirely sure it's possible using SQL Server queries. This may be some business logic (thanks to the product codes) that needs implemented in code. Something like the Entity Framework or Linq-to-SQL may be better suited to get you the data you're looking for.
How about adding AND LEFT(prdcod, 2) = 'F-'?
Try replacing the "-" with a space so the order is what you would expect:
DECLARE #list table(word varchar(50))
--create list
INSERT INTO #list
SELECT 'F-DH1'
UNION ALL
SELECT 'F-DH2'
UNION ALL
SELECT 'F-DH3'
UNION ALL
SELECT 'FET-RAZ'
UNION ALL
SELECT 'F-FMC'
UNION ALL
SELECT 'F-FML'
UNION ALL
SELECT 'F-FMS'
--original order
SELECT * FROM #list order by word
--show how order changes
SELECT *,replace(word,'-',' ') FROM #list order by replace(word,'-',' ')
--show between condition
SELECT * FROM #list where replace(word,'-',' ') between 'F DH1' and 'F FMS'

SELECT from nothing?

Is it possible to have a statement like
SELECT "Hello world"
WHERE 1 = 1
in SQL?
The main thing I want to know, is can I SELECT from nothing, ie not have a FROM clause.
It's not consistent across vendors - Oracle, MySQL, and DB2 support dual:
SELECT 'Hello world'
FROM DUAL
...while SQL Server, PostgreSQL, and SQLite don't require the FROM DUAL:
SELECT 'Hello world'
MySQL does support both ways.
Try this.
Single:
SELECT * FROM (VALUES ('Hello world')) t1 (col1) WHERE 1 = 1
Multi:
SELECT * FROM (VALUES ('Hello world'),('Hello world'),('Hello world')) t1 (col1) WHERE 1 = 1
more detail here : http://modern-sql.com/use-case/select-without-from
In Oracle:
SELECT 'Hello world' FROM dual
Dual equivalent in SQL Server:
SELECT 'Hello world'
Here is the most complete list of database support of dual from https://blog.jooq.org/tag/dual-table/:
In many other RDBMS, there is no need for dummy tables, as you can
issue statements like these:
SELECT 1;
SELECT 1 + 1;
SELECT SQRT(2);
These are the RDBMS, where the above is generally possible:
H2
MySQL
Ingres
Postgres
SQLite
SQL Server
Sybase ASE
In other RDBMS, dummy tables are required, like in Oracle. Hence,
you’ll need to write things like these:
SELECT 1 FROM DUAL;
SELECT 1 + 1 FROM DUAL;
SELECT SQRT(2) FROM DUAL;
These are the RDBMS and their respective dummy tables:
DB2: SYSIBM.DUAL
Derby: SYSIBM.SYSDUMMY1
H2: Optionally supports DUAL
HSQLDB: INFORMATION_SCHEMA.SYSTEM_USERS
MySQL: Optionally supports DUAL
Oracle: DUAL
Sybase SQL Anywhere: SYS.DUMMY
Ingres has no DUAL, but would actually need it as in Ingres you cannot
have a WHERE, GROUP BY or HAVING clause without a FROM clause.
In SQL Server type:
Select 'Your Text'
There is no need for the FROM or WHERE clause.
You can. I'm using the following lines in a StackExchange Data Explorer query:
SELECT
(SELECT COUNT(*) FROM VotesOnPosts WHERE VoteTypeName = 'UpMod' AND UserId = #UserID AND PostTypeId = 2) AS TotalUpVotes,
(SELECT COUNT(*) FROM Answers WHERE UserId = #UserID) AS TotalAnswers
The Data Exchange uses Transact-SQL (the SQL Server proprietary extensions to SQL).
You can try it yourself by running a query like:
SELECT 'Hello world'
There is another possibility - standalone VALUES():
VALUES ('Hello World');
Output:
column1
Hello World
It is useful when you need to specify multiple values in compact way:
VALUES (1, 'a'), (2, 'b'), (3, 'c');
Output:
column1 column2
1 a
2 b
3 c
DBFiddle Demo
This syntax is supported by SQLite/PostgreSQL/DB LUW/MariaDB 10.3.
In Firebird, you can do this:
select "Hello world" from RDB$DATABASE;
RDB$DATABASE is a special table that always has one row.
I think it is not possible. Theoretically: select performs two sorts of things:
narrow/broaden the set (set-theory);
mapping the result.
The first one can be seen as a horizontal diminishing opposed to the where-clause which can be seen as a vertical diminishing. On the other hand, a join can augment the set horizontally where a union can augment the set vertically.
augmentation diminishing
horizontal join/select select
vertical union where/inner-join
The second one is a mapping. A mapping, is more a converter. In SQL it takes some fields and returns zero or more fields. In the select, you can use some aggregate functions like, sum, avg etc. Or take all the columnvalues an convert them to string. In C# linq, we say that a select accepts an object of type T and returns an object of type U.
I think the confusion comes by the fact that you can do: select 'howdy' from <table_name>. This feature is the mapping, the converter part of the select. You are not printing something, but converting! In your example:
SELECT "
WHERE 1 = 1
you are converting nothing/null into "Hello world" and you narrow the set of nothing / no table into one row, which, imho make no sense at all.
You may notice that, if you don't constrain the number of columns, "Hello world" is printed for each available row in the table. I hope, you understand why by now. Your select takes nothing from the available columns and creates one column with the text: "Hello world".
So, my answer is NO. You can't just leave out the from-clause because the select always needs table-columns to perform on.
In Standard SQL, no. A WHERE clause implies a table expression.
From the SQL-92 spec:
7.6 "where clause"
Function
Specify a table derived by the
application of a "search condition" to
the result of the preceding "from
clause".
In turn:
7.4 "from clause"
Function
Specify a table derived from one or more named tables.
A Standard way of doing it (i.e. should work on any SQL product):
SELECT DISTINCT 'Hello world' AS new_value
FROM AnyTableWithOneOrMoreRows
WHERE 1 = 1;
...assuming you want to change the WHERE clause to something more meaningful, otherwise it can be omitted.
For ClickHouse, the nothing is system.one
SELECT 1 FROM system.one
I know this is an old question but the best workaround for your question is using a dummy subquery:
SELECT 'Hello World'
FROM (SELECT name='Nothing') n
WHERE 1=1
This way you can have WHERE and any clause (like Joins or Apply, etc.) after the select statement since the dummy subquery forces the use of the FROM clause without changing the result.
For DB2:
`VALUES('Hello world')`
You can do multiple "rows" as well:
`VALUES('Hello world'),('Goodbye world');`
You can even use them in joins as long as the types match:
VALUES(1,'Hello world')
UNION ALL
VALUES(2,'Goodbye world');
I'm using firebird
First of all, create a one column table named "NoTable" like this
CREATE TABLE NOTABLE
(
NOCOLUMN INTEGER
);
INSERT INTO NOTABLE VALUES (0); -- You can put any value
now you can write this
select 'hello world' as name
from notable
you can add any column you want to be shown