Why does the sql select statement fail? - sql

In the scenario below, the final select from the Combine view fails, any ideas why?
The Subset table does not have a row that corresponds to the one in MasterCodes that wouldn't cast to an integer value.
CREATE TABLE MasterCodes (
ID INT
, Code VARCHAR(10) )
GO
CREATE TABLE Subset (
ID INT )
GO
CREATE VIEW Combine AS
SELECT S.ID
, M.Code
, CAST(M.Code AS INT) IntCode
FROM Subset S
INNER JOIN MasterCodes M ON M.ID = S.ID
GO
INSERT MasterCodes (ID, Code) VALUES (1, '1')
INSERT MasterCodes (ID, Code) VALUES (2, '2')
INSERT MasterCodes (ID, Code) VALUES (3, 'three')
INSERT MasterCodes (ID, Code) VALUES (4, '4')
INSERT Subset (ID) VALUES (1)
INSERT Subset (ID) VALUES (2)
INSERT Subset (ID) VALUES (4)
SELECT * FROM Combine -- 3 rows returned
SELECT * FROM Combine WHERE Code = '2' -- 1 row returned
SELECT * FROM Combine WHERE Code = '3' -- 0 rows returned
SELECT * FROM Combine WHERE IntCode = 2 -- fails, error msg is
Msg 245, Level 16, State 1, Line 15
Conversion failed when converting the varchar value 'three' to data type int.
Environment is Sql2k5 Standard (64-bit) ON Win2k3 Server R2

Probably because in some row, M.Code contains the literal word "three", and the view is trying to cast a non-numeric looking word into an int (you can probably cast "3" into an int, but not "puppy" or "three", etc.).
Edit: Added a comment, but worthwhile to add it here. SQL Server is going to try and execute and join the as efficiently as possible, and it's going try and apply the where clause apparently even before joining.
This makes sense if you consider that VIEWs nowadays work almost fully like a real table. It has to do something SIMILAR to this; otherwise, it will join everything and return all values BEFORE being filtered out.
Hideously expensive.
What I'm not sure about is if the execution plan will show this level of detail.

You're inserting the string "three" into MasterCodes.Code, and your view is attempting to cast this value to an integer. This SQL should give the same error:
select cast("three" as int)
Solution? Replace "three" with "3" in your insert statement.

Because when you try to use IntCode in your condition logic the view try's to cast "three" as an int.
Use isnumeric() with a case statement to create the view
case when isnumeric(field) then
cast(field as int)
else
null
end AS IntCode

If your result set is accurate:
"SELECT * FROM Combine WHERE Code = '2' -- 1 row returned
SELECT * FROM Combine WHERE Code = '3' -- 0 rows returned
SELECT * FROM Combine WHERE IntCode = 2 -- fails, error msg isMsg 245, Level 16, State 1, Line 15Conversion failed when converting the varchar value 'three' to data type int."
then the only time it fails is when you try to compare against IntCode field, it almost seems like it is failing when it tries to put the non-numeric value on the left side of the "IntCode = 2" comparison, because this is the only time it will need to pick up every single value in the code field.
Hope that helps!

Related

FoxPro Conditional Sum Statement

I am trying to perform a conditional sum on a column of data in a FoxPro program. I think it should be able to be done in one line or so. So far I have something that looks like:
public sumvar
sumvar = SUM(TABLE2.val WHERE table2.id = table1.id)
This is being done inside a loop, so for this instance, table1.id might equal 4, and there are say 3 rows in table2 which have table2.id = 4. So I want to sum the table2.val value for just these 3 rows together. Any help much appreciated!
I'd recommend the SQL way rather than the xBase Sum command
CLEAR
CREATE CURSOR test (id Int, amount N(15,2))
INSERT INTO test VALUES (1, 15.45)
INSERT INTO test VALUES (1, 8)
INSERT INTO test VALUES (2, 9)
GO TOP IN test
* SQL
LOCAL ARRAY laSum[1]
SELECT SUM(amount) FROM test WHERE id = 1 INTO ARRAY laSum
? laSum[1]
* xBase
SUM amount FOR id = 1 TO result
? m.result
* F1 Help says: "Totals all or specified numeric fields in the **currently selected table**"
Another disadvantage of the xBase command would be moving record pointers.

Using CASE statement to INSERT values into multiple columns in SQLite

I'm trying to use INSERT statement to add values to a few columns in a new table depending on the value of a column in another table. I'm writing in SQLite. I was inspired by this question in sqlite update trigger with multiple if/Case Conditions which used CASE statement in one of the columns, but I would like to apply to all the columns that I'm adding.
The code I have wrote is similar to:
CREATE TABLE machine_info (
MachineType TEXT,
MaxPower INTEGER,
Frequency REAL);
INSERT INTO machine_info (MachineType, MaxPower, Frequency)
VALUES (
CASE WHEN parts.Machine = "A" THEN "Diode", 200, 60
WHEN parts.Machine = "B" THEN "AC", 500, 50
WHEN parts.Machine = "C" THEN NULL, 500, NULL
);
And the error message says Result: near ",": syntax error. Did I wrap the CASE statement at the wrong place?
You may use an INSERT INTO ... SELECT, but you'll need CASE expressions for each of the 3 columns:
INSERT INTO machine_info (MachineType, MaxPower, Frequency)
SELECT
CASE Machine WHEN 'A' THEN 'Diode' WHEN 'B' THEN 'AC' END,
CASE Machine WHEN 'A' THEN 200 WHEN 'B' THEN 500 WHEN 'C' 500 END,
CASE Machine WHEN 'A' THEN 60 WHEN 'B' THEN 50 END
FROM machine_info
WHERE Machine IN ('A', 'B', 'C');

Converting a column value from real data type to string during pattern matching in SQL query

I have a table in SQL database named 'customer' which has a column name called 'customer_no' of datatype real. data contained in the 'customer_no' column is as follows:
customer_no
........
........
54213154
11011011
11011012
11011013
11011014
11011215
11011015
11011101
45121478
........
........
I want to retrieve the column values that begin with 1101 using wildcard pattern matching. I am using SQL Server Management Studio. I have made the following SQL query to get the result:
select * from customer
where CONVERT(VARCHAR(100), customer_no) like '1101%';
But the problem is, I am getting 0 rows as output. Is there anything wrong in the above query? How can I get the desired result?
If customer number is an integer, why not use:
where customer_no >= 11010000 and
customer_no < 11020000
you need to convert the real to numeric first
CREATE TABLE #customer
(
customer_no real
)
INSERT INTO #customer(customer_no) SELECT 54213154
INSERT INTO #customer(customer_no) SELECT 11011011
INSERT INTO #customer(customer_no) SELECT 11011012
INSERT INTO #customer(customer_no) SELECT 11011013
INSERT INTO #customer(customer_no) SELECT 11011014
INSERT INTO #customer(customer_no) SELECT 11011215
INSERT INTO #customer(customer_no) SELECT 11011015
INSERT INTO #customer(customer_no) SELECT 11011101
INSERT INTO #customer(customer_no) SELECT 11021014
INSERT INTO #customer(customer_no) SELECT 45121478
SELECT * FROM #customer
WHERE CONVERT(VARCHAR(100),CONVERT(NUMERIC, customer_no)) like '1101%';
DROP TABLE #customer
When the values are converted to a characters representation it's using scientific notation which is why the pattern doesn't match.
Will math work?
where cast(floor(customer_no) as int) /
cast(power(10, floor(log10(customer_no) - 3)) as int) = 1101
-- note that floor(log10(1101)) = 3
The other advice you've gotten seems sound so this would really only apply if, for instance, the length of the id is variable. Using a floating point value for an id is only asking for headaches.

INSERT SELECT FROM VALUES casting

It's often desirable to INSERT from a SELECT expression (e.g. to qualify with a WHERE clause), but this can get postgresql confused about the column types.
Example:
CREATE TABLE example (a uuid primary key, b numeric);
INSERT INTO example
SELECT a, b
FROM (VALUES ('d853b5a8-d453-11e7-9296-cec278b6b50a', NULL)) as data(a,b);
=> ERROR: column "a" is of type uuid but expression is of type text
This can be fixed by explicitly casting in the values:
INSERT INTO example
SELECT a, b
FROM (VALUES ('d853b5a8-d453-11e7-9296-cec278b6b50a'::uuid, NULL::numeric)) as data(a,b);
But that's messy and a maintenance burden. Is there some way to make postgres understand that the VALUES expression has the same type as a table row, i.e. something like
VALUES('d853b5a8-d453-11e7-9296-cec278b6b50a', NULL)::example%ROWTYPE
Edit:
The suggestion of using (data::example).* is neat, but unfortunately it complete seems to screw up the postgres query planner when combined with a WHERE clause like so:
INSERT INTO example
SELECT (data::example).*
FROM (VALUES ('d853b5a8-d453-11e7-9296-cec278b6b50a', NULL)) as data
WHERE NOT EXISTS (SELECT * FROM example
WHERE (data::example)
IS NOT DISTINCT FROM example);
This takes minutes with a large table.
You can cast a record to a row type of your table:
INSERT INTO example
SELECT (data::example).*
FROM (
VALUES
('d853b5a8-d453-11e7-9296-cec278b6b50a', NULL),
('54514c89-f188-490a-abbb-268f9154ab2c', 42)
) as data;
data::example casts the complete row to a record of type example. The (...).* then turns that into the columns defined in the table type example
You could use VALUES directly:
INSERT INTO example(a, b)
VALUES ('d853b5a8-d453-11e7-9296-cec278b6b50a', NULL);
DBFiddle Demo
Or just cast once:
INSERT INTO example(a, b)
SELECT a::uuid, b::numeric
FROM (VALUES ('d853b5a8-d453-11e7-9296-cec278b6b50a', NULL),
('bb53b5a8-d453-11e7-9296-cec278b6b50a',1) ) as data(a,b);
DBFiddle Demo2
Note, please always explicitly define columns list.

How to alter the boolean value in SQL Server select query?

Basically I want to alter the boolean value selecting from the table:
e.g.:
SELECT otherColumns, not ysnPending FROM table
I need a column ysnPending = true if the value is false & false if the value is true.
Is there any function available to alter the Boolean value or I should use IIf or CASE...?
use CASE, or if the bit field is non-nullable you could just subtract from 1.
SELECT
otherColumns,
(1 - ysnPending) -- NOT ysnPending
FROM table
(Using CASE might lead to more understandable code.)
If ysnPending is nullable, what behaviour do you assign to NOT?
Example using a case statement :
create table table1 (id int not null, ysnPending bit null)
insert table1 values (1, 1)
insert table1 values (2, null)
insert table1 values (3, 0)
select id, cast((case when ysnPending = 1 then 0 else 1 end) as bit) as Not_ysnPending from table1
Assumes you want 1 returned when ysnPending is NULL.
The cast to bit type is to make sure that the returned column is of a BIT datatype. If you leave it out, it will return an INTEGER type. (This may or may not matter to you, depending on how exactly you are going to use the returned result set).