SELECT-FROM-IF condition in SQL - sql

I have the following SELECT statement:
SELECT
"project_id" as "ID"
FROM "projects"
but I want this to happen only when I have only distinct values in this column, such as:
COUNT(DISTINCT "project_id") = COUNT("project_id")
else I would like the program to crash with a message "The IDs do not have unique values".
Any ideas how I should tackle this?
I tried different CASE WHEN scenarios but without any luck.

This is more complicated than you think because you are trying to return 2 different datasets from the same query - most RDBMS systems do not like this. The datatype of each column must always be the same, and the number of columns needs to be the same. What you are trying to do is really better suited to the application layer.
That being said, here is a version that is close to what you ask. I have written it using SQL Server but the actual query itself uses standard SQL. First I will make a table variable with some sample data.
DECLARE #Data TABLE (project_id INT);
-- fill the table with all unqiue values
INSERT INTO #Data(project_id) VALUES (1), (2), (3);
SELECT
CASE WHEN COUNT(DISTINCT project_id) = COUNT(project_id) THEN CAST(project_id AS VARCHAR)
ELSE CONCAT(project_id, ' is not unique') END AS [ID]
FROM #Data
GROUP BY project_id;
The output of this looks like:
|ID|
|--|
|1|
|2|
|3|
Now let's look at a version where there are duplicate values:
DECLARE #Data TABLE (project_id INT);
-- fill the table with all unqiue values
INSERT INTO #Data(project_id) VALUES (1), (2), (3);
-- add duplicate values
INSERT INTO #Data(project_id) VALUES (1), (3);
SELECT
CASE WHEN COUNT(DISTINCT project_id) = COUNT(project_id) THEN CAST(project_id AS VARCHAR)
ELSE CONCAT(project_id, ' is not unique') END AS [ID]
FROM #Data
GROUP BY project_id;
And the output looks like:
ID
1 is not unique
2
3 is not unique
Again, this isn't exactly what you asked for, but I am not 100% sure that what you are asking can be accomplished in SQL alone.

Related

How to get a key from another table in a insert into... Values SQL statement?

I am not very experienced in SQL statements and I am trying to combine a couple of statements to have less traffic to the db.(and to make sure no other actions can happen inbetween)...
I have two tables:
Table: R_LOTS
a.o. 2 columns: PK_R_LOT and LOTCODE
Table: R_LOTTRACKING
Columns: FK_R_LOT,TIMESTAMP,FK_MAGLOCATIES
I use the statement:
INSERT INTO R_LOTTRACKING (FK_R_LOT,TIMESTAMP,FK_MAGLOCATIES) VALUES (?,CURRENT_TIMESTAMP,?).
On the questionmarks I can fill in the values to send.
However, as you can imagine, I do not have the FK_R_LOT but I have the LOTCODE (of R_LOT).. Of course I can get the FK_R_LOT with a seperate SELECT PK_R_LOT FROM R_LOT WHERE LOTCODE=?; but is there a way to combine these statements?
I have seen some examples but then all information seems to come from the R_LOT table but I could not find a combination of VALUES and SELECT.
Summary:
I know: LOTCODE and FK_MAGLOCATIES
How to combine the statements to insert the row:
INSERT INTO R_LOTTRACKING (FK_R_LOT,TIMESTAMP,FK_MAGLOCATIES) VALUES (?,CURRENT_TIMESTAMP,?)
SELECT PK_R_LOT FROM R_LOT WHERE LOTCODE=?
Use a subquery:
INSERT INTO R_LOTTRACKING ( FK_R_LOT, ... )
VALUES ( (SELECT PK_R_LOT FROM R_LOT WHERE LOTCODE = ?), ... );
or use an insert select:
INSERT INTO R_LOTTRACKING ( FK_R_LOT, ... )
SELECT PK_R_LOT, ... FROM R_LOT WHERE LOTCODE = ?;

DELETE WITH INTERSECT

I have two tables with the same number of columns with no primary keys (I know, this is not my fault). Now I need to delete all rows from table A that exists in table B (they are equal, each one with 30 columns).
The most immediate way I thought is to do a INNER JOIN and solve my problem. But, write conditions for all columns (worrying about NULL) is not elegant (maybe cause my tables are not elegant either).
I want to use INTERSECT. I am not knowing how to do it? This is my first question:
I tried (SQL Fiddle):
declare #A table (value int, username varchar(20))
declare #B table (value int, username varchar(20))
insert into #A values (1, 'User 1'), (2, 'User 2'), (3, 'User 3'), (4, 'User 4')
insert into #B values (2, 'User 2'), (4, 'User 4'), (5, 'User 5')
DELETE #A
FROM (SELECT * FROM #A INTERSECT SELECT * from #B) A
But all rows were deleted from table #A.
This drived me to second question: why the command DELETE #A FROM #B deletes all rows from table #A?
Try this:
DELETE a
FROM #A a
WHERE EXISTS (SELECT a.* INTERSECT SELECT * FROM #B)
Delete from #A where, for each record in #A, there is a match where the record in #A intersects with a record in #B.
This is based on Paul White's blog post using INTERSECT for inequality checking.
SQL Fiddle
To answer your first question you can delete based on join:
delete a
from #a a
join #b b on a.value = b.value and a.username = b.username
The second case is really strange. I remember similar case here and many complaints about this behaviour. I will try to fing that question.
You can use Giorgi's answer to delete the rows you need.
As for the question regarding why all rows were deleted, that's because there is no limiting condition. Your FROM clause gets a table to process, but there is no WHERE clause to prevent certain rows from being deleted from #A.
Create a table (T) defining the primary keys
insert all records from A into T (i will assume there are no duplicates in A)
try to insert all records from B in T
3A. if insert fails delete it from B (already exists)
Drop T (you really shouldn't !!!)
Giorgi's answer explicitly compares all columns, which you wanted to avoid.
It is possible to write code that doesn't list all columns explicitly.
EXCEPT produces the result set that you need, but I don't know a good way to use this result set to DELETE original rows from A without primary key. So, the solution below saves this intermediary result in a temporary table using SELECT * INTO. Then deletes everything from A and copies temporary result into A. Wrap it in a transaction.
-- generate the final result set that we want to have and save it in a temporary table
SELECT *
INTO #t
FROM
(
SELECT * FROM #A
EXCEPT
SELECT * FROM #B
) AS E;
-- copy temporary result back into A
DELETE FROM #A;
INSERT INTO #A
SELECT * FROM #t;
DROP TABLE #t;
-- check the result
SELECT * FROM #A;
result set
value username
1 User 1
3 User 3
The good side of this solution is that it uses * instead of the full list of columns. Of course, you can list all columns explicitly as well. It will still be easier to write and handle, than writing comparisons of all columns and taking care of possible NULLs.

SQL Server - Contain Multiples Values

I need retrieve a value of columm with SELECT. But, I have multiple values ...
I don't know what the user go select in checkbox...
Ex:
Insert Into MyTable (dados) Values ('a1') I want the result = Angulo 1
Insert Into MyTable (dados) Values ('a2';'a3') I want the result = Angulo 2
Insert into MyTable (dados) Values ('a3'; a1) I want the result = Angulo 3; Angulo 1
Insert into MyTable (dados) Values ('a6'; 'a7'; 'a4') I want the result = Angulo 6; Angulo 7;Angulo4
I am Trying with SELECT CASE WHEN. But it still fails...
I suspect you are asking how to use the IN keyword in your SELECT statements? It is a little unclear what you are trying to do.
Try this:
SELECT *
FROM MyTable
WHERE dados IN ('a6','a7','a4')
Assuming you have a table named MyTable and a column named dados with 3 rows in that table for a6, a7 and a4, this will return all the matches (in this case, all three rows).
Good luck.
When you say:
insert into MyTable(dados)
Values ('a6', 'a7', 'a4')
You are saying "I have one column to put data into called dados." Then, you are providing three values. This will fail in any database (even apart from the fact that the semicolons should be commas).
Perhaps you want:
insert into MyTable(dados)
Values ('a6;a7;a4')
That is only one value, a string.
This suggests a denormalized database. You might want three different rows in a table, one for each value, connected together by some key.
here are some examples if you're using sql server 2008 and above:
if(OBJECT_ID('tempdb..#dados') is not null)
DROP TABLE #dados
select top 100 * INTO #dados FROM
(
values(1,2,3),
(4,5,6),
(7,8,9)
) t(a,b,c)
select * FROM #dados
INSERT INTO #dados (a,b,c)
values(11,22,33),
(44,55,66),
(77,88,99)
SELECT * FROM #dados
INSERT INTO #dados (a,b,c)
SELECT * FROM
(
values(111,222,333),
(444,555,666),
(777,888,999)
) t(a,b,c)
SELECT * FROM #dados
If you want to insert multiple rows (not columns) the syntax is
Insert Into
MyTable (dados)
Values
('a1'),
('a2')
Looks like you're trying to ask for two things.
How to insert multiple values would be done in the following way:
Insert Into MyTable (dados) Values ('a6'),('a7'),('a4')
If you want to return the actual values 'Angulo' + the number, you can use the following:
CREATE TABLE MyTable
(
Dados varchar(255)
)
Insert Into MyTable (dados) Values ('a12')
Insert Into MyTable (dados) Values ('a2'),('a3')
Insert Into MyTable (dados) Values ('a3'),('a1')
Insert Into MyTable (dados) Values ('a6'),('a7'),('a4')
SELECT 'Angulo'+ SUBSTRING(dados,PATINDEX('%[0-9]%',dados),LEN(dados))
FROM MyTable
It will find the first number (assuming it's always the first number you're after) and get the rest of them. It will then append it with the prefix 'Angulo' (e.g Angulo1, Angulo7, etc)
If these aren't what you're after. Please can you explain further what you need.

Sql server order by value not by field name

suppose my table structure is like
ID OEReference
--- ------------
1 00000634B9
2 00000634B6
3 0005000053
4 0002855071
5 0000940148
6 0001414825
7 00000634B9
i want that they way i supply OEReference that order should maintain in output.
my sql is like
Select * from mytable where OEReference in ('00000634B9','0001414825','00000634B6')
the above statement did not return resultset according to the order of IN clause. i know that it is not possible by ORDER BY CLAUSE
how can i do it with simple sql statement in sql server. thanks
You could use a temporary table as a filter. Aninner join will enforce the filter, and you can sort on the identity column:
declare #filter table (id int identity, ref varchar(50))
insert #filter values ('00000634B9')
insert #filter values ('0001414825')
insert #filter values ('00000634B6')
select *
from YourTable yt
join #filter filter
on filter.ref = yt.OEReference
order by
filter.id
Please here is my solution for you:
SELECT [id], [OEReference]
FROM [Tbl]
where [OEReference] in ('002', '001')
order by case [OEReference]
when '002' then 1
when '001' then 2
end
Please note: this can decrease performance of your server. It depends on how many rows in table you have. However, you can easily add index for OEReference. Off course, you should generate such query dynamically.
My solution is not close ideal. Maybe you found it useful for you.
Happy coding!

Looking for SQL constraint: SELECT COUNT(*) from tBoss < 2

I'd like to limit the entries in a table. Let's say in table tBoss. Is there a SQL constraint that checks how many tuples are currently in the table? Like
SELECT COUNT(*) from tBoss < 2
Firebird says:
Invalid token.
Dynamic SQL Error.
SQL error code = -104.
Token unknown - line 3, column 8.
SELECT.
You could do this with a check constraint and a scalar function. Here's how I built a sample.
First, create a table:
CREATE TABLE MyTable
(
MyTableId int not null identity(1,1)
,MyName varchar(100) not null
)
Then create a function for that table. (You could maybe add the row count limit as a parameters if you want more flexibility.)
CREATE FUNCTION dbo.MyTableRowCount()
RETURNS int
AS
BEGIN
DECLARE #HowMany int
SELECT #HowMany = count(*)
from MyTable
RETURN #HowMany
END
Now add a check constraint using this function to the table
ALTER TABLE MyTable
add constraint CK_MyTable__TwoRowsMax
check (dbo.MyTableRowCount() < 3)
And test it:
INSERT MyTable (MyName) values ('Row one')
INSERT MyTable (MyName) values ('Row two')
INSERT MyTable (MyName) values ('Row three')
INSERT MyTable (MyName) values ('Row four')
A disadvantage is that every time you insert to the table, you have to run the function and perform a table scan... but so what, the table (with clustered index) occupies two pages max. The real disadvantage is that it looks kind of goofy... but everything looks goofy when you don't understand why it has to be that way.
(The trigger solution would work, but I like to avoid triggers whenever possible.)
Does your database have triggers? If so, Add a trigger that rolls back any insert that would add more than 2 rows...
Create Trigger MyTrigName
For Insert On tBoss
As
If (Select Count(*) From tBoss) > 2
RollBack Transaction
but to answer your question directly, the predicate you want is to just put the select subquery inside parentheses. like this ...
[First part of sql statement ]
Where (SELECT COUNT(*) from tBoss) < 2
To find multiples in a database your best bet is a sub-query for example: (Note I am assuming you are looking to find duplicated rows of some sort)
SELECT id FROM tBoss WHERE id IN ( SELECT id FROM tBoss GROUP BY id HAVING count(*) > 1 )
where id is the possibly duplicated column
SELECT COUNT(*) FROM tBoss WHERE someField < 2 GROUP BY someUniqueField