How to write a Sql statement without using union? - sql

I have a sql statement like below. How can I add a single row(code = 0, desc = 1) to result of this sql statement without using union keyword? thanks.
select code, desc
from material
where material.ExpireDate ='2010/07/23'

You can always create a view for your table which itself uses UNION keyword
CREATE VIEW material_view AS SELECT code, desc, ExpireDate FROM material UNION SELECT '0', '1', NULL;
SELECT code, desc FROM material_view WHERE ExpireDate = '2010/07/23' OR code = '0';

WITH material AS
(
SELECT *
FROM
(VALUES (2, 'x', '2010/07/23'),
(3, 'y', '2009/01/01'),
(4, 'z', '2010/07/23')) vals (code, [desc], ExpireDate)
)
SELECT
COALESCE(m.code,x.code) AS code,
COALESCE(m.[desc],x.[desc]) AS [desc]
FROM material m
FULL OUTER JOIN (SELECT 0 AS code, '1' AS [desc] ) x ON 1=0
WHERE m.code IS NULL OR m.ExpireDate ='2010/07/23'
Gives
code desc
----------- ----
2 x
4 z
0 1

Since you don't want to use either a union or a view, I'd suggest adding a dummy row to the material table (with code = 0, desc = 1, and ExpireDate something that would never normally be selected - eg. 01 January 1900) - then use a query like the following:
select code, desc
from material
where material.ExpireDate ='2010/07/23' or
material.ExpireDate ='1900/01/01'
Normally, a Union would be my preferred option.

Related

ORDER BY and UNION

So I'm trying to union two queries and then order by a column. However, whenever I try and run the query it gives an error that doesn't make sense to me.
Example data
CREATE TABLE test (
Company_code VARCHAR(120) NOT NULL,
operating_year INT NOT NULL,
Profit INT NOT NULL
);
INSERT INTO test (Company_code, operating_year, Profit)
VALUES ('A', 1999, 2000),
('A', 2000, 3000),
('B', 1999, 1600),
('B', 2000, 4000);
Query
SELECT
t.company_code,
t.profit
FROM
test t
WHERE
t.company_code = 'A'
UNION
SELECT
t.company_code,
t.profit
FROM
test t
WHERE
t.company_code = 'B'
ORDER BY
-- t.profit; --- Does *not* work
-- profit; --- Does work
Ignore the very basic example, and how just adding an OR to the WHERE statement resolves this.
My question is why does having an alias in the ORDER BY throw an error when a UNION is involved. But not when run individually?
My question is why does having an alias (t) in the ORDER BY throw an error when a UNION is involved. But not when run individually.
When you use a set-theoretic operator between two queries (e.g. UNION, UNION ALL, EXCEPT, INTERSECT, etc) the ORDER BY clause now applies to the end result of that operator, which is a new anonymous derived-table. So there's no way to bind t in ORDER BY because it is now out-of-scope.
If you add parentheses around the derived-tables it's easier to see why... so your query is really like this pseudo-SQL:
SELECT
company_code,
profit
FROM
(
SELECT
t.company_code,
t.profit
FROM
test t
WHERE
t.company_code = 'A'
) AS q1
UNION
(
SELECT
t.company_code,
t.profit
FROM
test t
WHERE
t.company_code = 'B'
) AS q2
ORDER BY
t.profit <-- `t` is not in scope. It's masked by (or "buried inside"?) q1 and q2.
profit <-- this works because `profit` is in the result of the UNION
Because you're not sorting the separate queries that reference columns through a table alias. You are ordering the UNION query output which is almost like a table itself.
The ORDER BY is performed after the UNION this is equivalent:
SELECT * FROM (
SELECT
t.company_code,
t.profit
FROM test t
WHERE t.company_code = 'A'
UNION
SELECT
t.company_code,
t.profit
FROM test t
WHERE t.company_code = 'B'
) t
ORDER BY t.profit; --- Should work
--ORDER BY profit; --- Does work

Group by absorb NULL unless it's the only value

I'm trying to group by a primary column and a secondary column. I want to ignore NULL in the secondary column unless it's the only value.
CREATE TABLE #tempx1 ( Id INT, [Foo] VARCHAR(10), OtherKeyId INT );
INSERT INTO #tempx1 ([Id],[Foo],[OtherKeyId]) VALUES
(1, 'A', NULL),
(2, 'B', NULL),
(3, 'B', 1),
(4, 'C', NULL),
(5, 'C', 1),
(6, 'C', 2);
I'm trying to get output like
Foo OtherKeyId
A NULL
B 1
C 1
C 2
This question is similar, but takes the MAX of the column I want, so it ignores other non-NULL values and won't work.
I tried to work out something based on this question, but I don't quite understand what that query does and can't get my output to work
-- Doesn't include Foo='A', creates duplicates for 'B' and 'C'
WITH cte AS (
SELECT *,
ROW_NUMBER() OVER (PARTITION BY [Foo] ORDER BY [OtherKeyId]) rn1
FROM #tempx1
)
SELECT c1.[Foo], c1.[OtherKeyId], c1.rn1
FROM cte c1
INNER JOIN cte c2 ON c2.[OtherKeyId] = c1.[OtherKeyId] AND c2.rn1 = c1.rn1
This is for a modern SQL Server: Microsoft SQL Server 2019
You can use a GROUP BY expression with HAVING clause like below one
SELECT [Foo],[OtherKeyId]
FROM #tempx1 t
GROUP BY [Foo],[OtherKeyId]
HAVING SUM(CASE WHEN [OtherKeyId] IS NULL THEN 0 END) IS NULL
OR ( SELECT COUNT(*) FROM #tempx1 WHERE [Foo] = t.[Foo] ) = 1
Demo
Hmmm . . . I think you want filtering:
select t.*
from #tempx1 t
where t.otherkeyid is not null or
not exists (select 1
from #tempx1 t2
where t2.foo = t.foo and t2.otherkeyid is not null
);
My actual problem is a bit more complicated than presented here, I ended up using the idea from Barbaros Özhan solution to count the number of items. This ends up with two inner queries on the data set with two different GROUP BY. I'm able to get the results I need on my real dataset using a query like the following:
SELECT
a.[Foo],
b.[OtherKeyId]
FROM (
SELECT
[Foo],
COUNT([OtherKeyId]) [C]
FROM #tempx1 t
GROUP BY [Foo]
) a
JOIN (
SELECT
[Foo],
[OtherKeyId]
FROM #tempx1 t
GROUP BY [Foo], [OtherKeyId]
) b ON b.[Foo] = a.[Foo]
WHERE
(b.[OtherKeyId] IS NULL AND a.[C] = 0)
OR (b.[OtherKeyId] IS NOT NULL AND a.[C] > 0)

Query to find if a aggregate string contains certain numbers

I am working on Big Query Standard SQL. I have a data table like shown below (using ; as separator):
id;operation
107327;-1,-1,-1,-1,5,-1,0,2,-1
108296;-1,6,2,-1,-1,-1
690481;0,-1,-1,-1,5
102643;5,-1,-1,-1,-1,-2,2,3,-1,0,-1,-1,-1,-1,-1,-1
103171;0,5
789481;0,-1,5
I would like to take id that only contains operation 0,5 or 0,-1,5 so the result will show:
690481
103171
789481
Below is for BigQuery Standard SQL
#standardSQL
SELECT *
FROM `project.dataset.table`
WHERE 0 = (
SELECT COUNT(1)
FROM UNNEST(SPLIT(operation)) op
WHERE NOT op IN ('0', '-1', '5')
)
You can test, play with above using sample data form your question as in below example
#standardSQL
WITH `project.dataset.table` AS (
SELECT 107327 id, '-1,-1,-1,-1,5,-1,0,2,-1' operation UNION ALL
SELECT 108296, '-1,6,2,-1,-1,-1' UNION ALL
SELECT 690481, '0,-1,-1,-1,5' UNION ALL
SELECT 102643, '5,-1,-1,-1,-1,-2,2,3,-1,0,-1,-1,-1,-1,-1,-1' UNION ALL
SELECT 103171, '0,5' UNION ALL
SELECT 789481, '0,-1,5'
)
SELECT *
FROM `project.dataset.table`
WHERE 0 = (
SELECT COUNT(1)
FROM UNNEST(SPLIT(operation)) op
WHERE NOT op IN ('0', '-1', '5')
)
with output
I think regular expression does what you want:
select t.*
from t
where regexp_contains(operation, '^0,(-1,)*5$');
If you want matches to rows that contain only 0, -1, or 5, you would use:
where regexp_contains(operation, '^((0|-1|5),)*(0|-1|5)$');

Get only best ranked rows from a subquery

I want to get the price of an article for a specific customer.
There are several levels of prices which i ranked in my query.
So Article A has a price on rank 1, 4, 6. The result should always be the lowest ranked price.
Article B rank 3 ,5
So article A price is ranked 1 and Article b is price ranked 3.
My query is below .
SELECT p2.* FROM(
SElect ART_ID, MIN(RANG) RANG FROM (
Select p.ART_ID, p.betrag ,
CASE p.PREIS_EBENE WHEN 'KA' THEN 1 WHEN 'KW' THEN 2 WHEN 'W' THEN 7 WHEN 'A' THEN 6 ELSE 99 END RANG
FROM MDART a
INNER JOIN MDPRSVK p ON (a.KLIENT_ID = p.KLIENT_ID AND a.ART_ID = p.ART_ID)
WHERE ICP_KZ.IS_SET(KENNUNG_USER, 'P') = 1
ORDER BY RANG)
GROUP BY ART_ID) T
INNER JOIN MDPRSVK p2 ON (p2.ART_ID = T.ART_ID AND p2.PREIS_EBENE = p.PREIS_EBENE)
i want to have every article appearing only once in the result
You have tagged your request PL/SQL, so I guess your DBMS may be Oracle.
If I understand correctly, the table MDPRSVK contains several prices per ART_ID. And you want to select each ART_ID's best price (best to worst: 'KA' -> 'KW' -> 'A' -> 'W' -> any other PREIS_EBENE).
You can use a window function (ROW_NUMBER, RANK or DENSE_RANK) for this:
select *
from mdprsvk
order by row_number()
over (partition by art_id
order by decode(preis_ebene, 'KA', 1, 'KW', 2, 'A', 3, 'W', 4, 5))
fetch first row with ties;
This is standard SQL. In Oracle, FETCH FIRST is available as of version 12c. In earlier versions you'd use a subquery instead:
select *
from
(
select
mdprsvk.*,
row_number() over (partition by art_id
order by decode(preis_ebene, 'KA', 1, 'KW', 2, 'A', 3, 'W', 4, 5))
as rn
from mdprsvk
)
where rn = 1;
Or use OraclesKEEP FIRST`:
select art_id, max(betrag)
keep (dense_rank first
order by decode(preis_ebene, 'KA', 1, 'KW', 2, 'A', 3, 'W', 4, 5))
from mdprsvk
group by art_id;
It is not clear, how MDART comes into play. It looks like you want to restrict your results to articles for certain clients and KENNUNG_USER is the column in MDART to check. If so, add a WHERE clause:
where exists
(
select *
from mdart
where mdart.klient_id = mdprsvk.klient_id
and mdart.art_id = mdprsvk.art_id
and icp_kz.is_set(mdart.kennung_user, 'p') = 1
)
Or with IN instead of EXISTS:
where (klient_id, art_id) in
(
select klient_id, art_id
from mdart
where icp_kz.is_set(kennung_user, 'p') = 1
)

Why does this query result in a "missing FROM clause"?

Why below sql statement keeps getting missing FROM clause entry for table error?
How to adjust this?
WITH SUBID AS (
SELECT * FROM "B_COLLECTION"."COLL_C_RECORD"
),
TR AS (
SELECT * FROM "B_TRACE"."PERSONAL_TC_RECORD"
)
SELECT "SUBJECT_C_ID"
FROM "B_COLLECTION"."COLL_C_RECORD"
WHERE ( SUBID.SUBJECT_ID = TR.PERSONAL_S_ID )
AND ( TR.STATE_ID ='5' OR TR.STATE_ID = 'A' OR TR.STATE_ID = 'C');
You are declaring SUBID and TR just fine, but since these are tables, your select statement does not know them yet.
Here you need to enter SUBID and TR in FROM clauses of your query. It should look somewhat like
WITH SUBID AS (
SELECT * FROM "B_COLLECTION"."COLL_C_RECORD"
) ,
TR AS (
SELECT * FROM "B_TRACE"."PERSONAL_TC_RECORD"
)
SELECT "SUBJECT_C_ID" FROM SUBID
WHERE SUBID."SUBJECT_ID" IN
(SELECT "PERSONAL_S_ID" FROM TR
WHERE TR."STATE_ID" = '5'
OR TR."STATE_ID" = 'A'
OR TR."STATE_ID" = 'C');
That is why you use WITH Clause only on repeating queries. Here it would actually be much nicier to use:
SELECT "SUBJECT_ID" FROM "B_COLLECTION"."COLL_C_RECORD" SUBID
WHERE SUBID."SUBJECT_ID" IN
(SELECT "PERSONAL_S_ID" FROM "B_TRACE"."PERSONAL_TC_RECORD" TR
WHERE TR."STATE_ID" = '5'
OR TR."STATE_ID" = 'A'
OR TR."STATE_ID" = 'C');