How to get value without subqueries (on SQL-Server)? - sql

I have the following table on SQL Server:
ID Num
1 A
2 B
2 B
3 C
3 C
4 C
(Num is a numeric column - A, B, and C are standins for numeric values, for the purpose of this question)
How to get the value of A+B+C+C without using subqueries and CTE?
A - for 1, B - for 2, C - for 3, C - for 4.
The answer seems to sum(distinct Num), but distinct is by ID field!
Demo table:
create table test (ID int, Num int);
insert into test values (1, 10);
insert into test values (2, 100);
insert into test values (2, 100);
insert into test values (3, 1000);
insert into test values (3, 1000);
insert into test values (4, 1000);
The correct answer is 10+100+1000+1000 = 2110.

A random guess, using CTE to avoid the pointless subquery restriction:
With X as (Select Distinct Id, No From Test)
Select
Sum(No)
From X
Or using a derived table (which works in SQL 2000):
Select
Sum(No)
From (
Select Distinct
Id,
No
From
Test
) a;
http://sqlfiddle.com/#!3/77a6e/6

The solution:
select cast(sum(distinct Num + cast(0.00001 as number(38,19))/ID) as number(18,2))

Related

Using values inside a cte in snowflake queries

I'm trying to use values inside a cte in snowflake. For example, this:
WITH person (id, name)
AS (VALUES (1, 'Ted'), (2, 'Bill'))
SELECT *
FROM person;
works fine in (for example) postgres, but gives me the following error in snowflake:
SQL compilation error: syntax error line 2 at position 9 unexpected 'VALUES'. syntax error line 2 at position 17 unexpected '1'. syntax error line 2 at position 26 unexpected ','.
However, from snowflake documentation I can see VALUES syntax is supported, for example this works in snowflake:
SELECT * FROM (VALUES (1, 'Ted'), (2, 'Bill')) as person (id, name);
So I'm wondering how I'd get it to work inside a cte.
I would like the same output as I would get from postgres (using a cte)
id | name
----+------
1 | Ted
2 | Bill
(2 rows)
You were close
with person (id, name) as
(select * from values (1, 'ted'), (2, 'bill'))
select *
from person;
You have to add a SELECT statement inside your CTE
WITH person AS (
SELECT $1 AS id,
$2 AS name
FROM (VALUES (1, 'Ted'), (2, 'Bill'))
)
SELECT *
FROM person;
You can define table and column alias like this:
select * from (values (1, 'One'), (3, 'three')) as person (ID, NAME);
And then of course reference the table expression in a CTE:
WITH PERSON as
(
select * from (values (1, 'One'), (3, 'three')) as person (ID, NAME)
)
SELECT * FROM person;

Add missing rows within a table

I need a hint please, in my table it can happen that positions of an order is not written to the next ID.
Let's look at the table:
Pos 2 is missing in ID 3
ID
DOC
POSI
TOTAL
1
123
1
100
1
123
2
600
1
123
3
200
2
123
1
100
2
123
2
600
2
123
3
200
3
123
1
100
3
123
3
200
Is it possible to create a view using SQL that compares the individual IDs partitions with each other and appends the missing value from ID 2 to ID 3 as a row?
Maybe you have some keywords for me, if something like this is possible.
The hint would be: Use a join.
One way of approaching this is, that you select the key pairs that you expect and then left join the original table. Be conscious about the missing-value handling, since you have not specified in your question what should happen to those newly created entries.
Test Data
CREATE TABLE test (id INTEGER, doc INTEGER, posi INTEGER, total INTEGER);
INSERT INTO test VALUES (1, 123, 1, 100);
INSERT INTO test VALUES (1, 123, 2, 600);
INSERT INTO test VALUES (1, 123, 3, 200);
INSERT INTO test VALUES (2, 123, 1, 100);
INSERT INTO test VALUES (2, 123, 2, 600);
INSERT INTO test VALUES (2, 123, 3, 200);
INSERT INTO test VALUES (3, 123, 1, 100);
INSERT INTO test VALUES (3, 123, 3, 200);
The possible key combinations can be generated with a cross join:
SELECT DISTINCT a.id, b.posi
FROM test a, test b
And now join the original table:
WITH expected_lines AS (
SELECT DISTINCT a.id, b.posi
FROM test a, test b
)
SELECT el.id, el.posi, t.doc, t.total
FROM expected_lines el
LEFT JOIN test t ON el.id = t.id AND el.posi = t.posi
You did not describe further, what should happen with the now empty columns. As you may note DOC and TOTAL are null.
My educated guess would be, that you want to make DOC part of the key and assume a TOTAL of 0. If that's the case, you can go with the following:
WITH expected_lines AS (
SELECT DISTINCT a.id, b.posi, c.doc
FROM test a, test b, test c
)
SELECT el.id, el.posi, el.doc, ifnull(t.total, 0) total
FROM expected_lines el
LEFT JOIN test t ON el.id = t.id AND el.posi = t.posi AND el.doc = t.doc
Result

Getting SQL rows where >= 1 row have a certain value in another column

I've seen similarly worded questions, and I may be phrasing it wrong, but take the following example table:
a
b
1
5
2
6
3
7
1
8
2
8
2
9
1
10
2
10
3
10
And say I know beforehand that I have a values [1,2]. How can I get all values of b that the a values share? In the above, the result would be [8, 10]. If I had [1,3] for a, then I would get [10]. If I had [2] for a, I would get [6,8,9,10]
I imagine it would start something like SELECT b from tablename WHERE ...
You can use intersect
Schema and insert statements:
create table test(a int, b int);
insert into test values(1, 5);
insert into test values(2, 6);
insert into test values(3, 7);
insert into test values(1, 8);
insert into test values(2, 8);
insert into test values(2, 9);
insert into test values(1, 10);
insert into test values(2, 10);
insert into test values(3, 10);
Query1:
select b from test where a=1
intersect
select b from test where a=2
Output:
b
8
10
Query2:
select b from test where a=1
intersect
select b from test where a=3
Output:
b
10
Query3:
select b from test where a=2
Output:
b
6
8
9
10
db<>fiddle here
Create a CTE that returns the values of a that you want and filter the table for these values only.
Then group by b and in the HAVING clause filter the resultset so that only values of b that are associated to the values of a that you want are returned:
WITH cte(a) AS (VALUES (1), (2))
SELECT b
FROM tablename
WHERE a IN cte
GROUP BY b
HAVING COUNT(*) = (SELECT COUNT(*) FROM cte);
See the demo.

Return sum of matching rows as a separate field

I want to write a SQL query that will give me field C as the sum of field B for each unique row in field A. So for X, field B has 10 and 35, therefore field C would should 45 for both rows of X.
I need a query that will return the entire result as one "dataset" like below (instead of a query that just gives me field C). I think maybe I would need 2 queries? One to grab A and B results and then a 2nd query to select from results and SUM(expression) for field C?
You can use a window function SUM()OVER() or a SUM() within a subquery like
CREATE TABLE T(
A VARCHAR(10),
B INT
);
INSERT INTO T VALUES
('X', 10),
('Y', 15),
('Z', 40),
('X', 35),
('Y', 10);
SELECT *,
(SELECT SUM(B) FROM T WHERE A = TT.A) C,
SUM(B) OVER(PARTITION BY A) AnotherC
FROM T TT;
Here is a live demo
Looks like Sami posted while I was doing my copy/paste :) but here's a similar solution:
Declare #test Table
(
a varchar(10),
b int
)
Insert Into #test (a, b) Values ('X', 10);
Insert Into #test (a, b) Values ('Y', 15);
Insert Into #test (a, b) Values ('Z', 40);
Insert Into #test (a, b) Values ('X', 35);
Insert Into #test (a, b) Values ('Y', 10);
Select t.a, t.b, (Select sum(sq.b) From #test sq Where sq.a = t.a) as c
From #test t
The below given query is enough.
select A,B, SUM(B) OVER(PARTITION BY A) AS C FROM TABLE

Find rows with same ID and have a particular set of names

EDIT:
I have a table with 3 rows like so.
ID NAME REV
1 A 0
1 B 0
1 C 0
2 A 1
2 B 0
2 C 0
3 A 1
3 B 1
I want to find the ID wich has a particular set of Names and the REV is same
example:
Edit2: GBN's solution would have worked perfectly, but since i do not have the access to create new tables. The added constraint is that no new tables can be created.
if input = A,B then output is 3
if input = A ,B,C then output is 1 and not 1,2 since the rev level differs in 2.
The simplest way is to compare a COUNT per ID with the number of elements in your list:
SELECT
ID
FROM
MyTable
WHERE
NAME IN ('A', 'B', 'C')
GROUP BY
ID
HAVING
COUNT(*) = 3;
Note: ORDER BY isn't needed and goes after the HAVING if needed
Edit, with question update. In MySQL, it's easier to use a separate table for search terms
DROP TABLE IF EXISTS gbn;
CREATE TABLE gbn (ID INT, `name` VARCHAR(100), REV INT);
INSERT gbn VALUES (1, 'A', 0);
INSERT gbn VALUES (1, 'B', 0);
INSERT gbn VALUES (1, 'C', 0);
INSERT gbn VALUES (2, 'A', 1);
INSERT gbn VALUES (2, 'B', 0);
INSERT gbn VALUES (2, 'C', 0);
INSERT gbn VALUES (3, 'A', 0);
INSERT gbn VALUES (3, 'B', 0);
DROP TABLE IF EXISTS gbn1;
CREATE TABLE gbn1 ( `name` VARCHAR(100));
INSERT gbn1 VALUES ('A');
INSERT gbn1 VALUES ('B');
SELECT
gbn.ID
FROM
gbn
LEFT JOIN
gbn1 ON gbn.`name` = gbn1.`name`
GROUP BY
gbn.ID
HAVING
COUNT(*) = (SELECT COUNT(*) FROM gbn1)
AND MIN(gbn.REV) = MAX(gbn.REV);
INSERT gbn1 VALUES ('C');
SELECT
gbn.ID
FROM
gbn
LEFT JOIN
gbn1 ON gbn.`name` = gbn1.`name`
GROUP BY
gbn.ID
HAVING
COUNT(*) = (SELECT COUNT(*) FROM gbn1)
AND MIN(gbn.REV) = MAX(gbn.REV);
Edit 2, without extra table, use a derived (inline) table:
SELECT
gbn.ID
FROM
gbn
LEFT JOIN
(SELECT 'A' AS `name`
UNION ALL SELECT 'B'
UNION ALL SELECT 'C'
) gbn1 ON gbn.`name` = gbn1.`name`
GROUP BY
gbn.ID
HAVING
COUNT(*) = 3 -- matches number of elements in gbn1 derived table
AND MIN(gbn.REV) = MAX(gbn.REV);
Similar to gbn, but allowing for the possibility of duplicate ID/Name combinations:
SELECT ID
FROM MyTable
WHERE NAME IN ('A', 'B', 'C')
GROUP BY ID
HAVING COUNT(DISTINCT NAME) = 3;
OKAY!... I solved my problem ! I modified GBN's logic to do it without a search table using the IN clause
1 flaw with doing MAX(rev) = MIN(REV) is: if i have a data like so .
ID NAME REV
1 A 0
1 B 1
1 A 1
then when I use a query like
Select ID from TABLE
where NAME in {A,B}
groupby ID
having count(*) = 2
and MIN(REV) = MAX(REV)
it will not show me the ID 1 as the min and max are different and the count is 3.
So i simply add another column to the groupby
so the final query is
Select ID from TABLE
where NAME in {A,B}
groupby ID,REV
having count(*) = 2
and MIN(REV) = MAX(REV)
Thanks,to all that helped. !