How to get also the not existing values - sql

I've got a query like this
select column, count(*)
from mytable
where column in ('XXX','YYY','ZZZ',....)
group by column;
But I want also to get a row for values the aren't in the table.
Let's suppose that 'ZZZ' doesn't exist in mytable, I'd like to get:
COLUMN COUNT(*)
XXX 3
YYY 2
ZZZ 0 (or NULL)
Oracle version 10g
Thanks in advance
Mark

In general, you would need to have a second table which contains all the possible column values whose counts you want to appear in the output. For demo purposes only, we can use a CTE for that:
WITH vals AS (
SELECT 'XXX' AS val UNION ALL
SELECT 'YYY' UNION ALL
SELECT 'ZZZ'
)
SELECT t1.val, COUNT(t2.col) AS cnt
FROM vals t1
LEFT JOIN mytable t2
ON t2.col = t1.val
GROUP BY
t1.val;

Related

Create a view of a table with a column that has multiple values

I have a table (Table1) like the following:
Col1
Col2
First
Code1,Code2,Code3
Second
Code2
So Col2 can contain multiple values comma separated, I have another table (Table2) that contains this:
ColA
ColB
Code1
Value1
Code2
Vaue2
Code3
Vaue3
I need to create a view that joins the two tables (Table1 and Table2) and returns something like this:
Col1
Col2
First
Value1,Value2,Value3
Second
Value2
Is that possible? (I'm on Oracle DB if that helps.)
It's a violation of first normal form to have a list in a column value like that. It causes a lot of difficulties in a relational database, like the one you are encountering now.
However, you can get what you want by using the LIKE operator to find colA values that are substrings of the Col2 column. Add delimiters before and after to catch the first and last ones. Then aggregate back up to a single list using LISTAGG.
SELECT table1.col1,
LISTAGG(table2.colB,',') WITHIN GROUP (ORDER BY table2.colB) value_list
FROM table1,
table2
WHERE ','||table1.col2||',' LIKE '%,'||table2.colA||',%'
GROUP BY table1.col1
This will not perform well on large volumes, because without an equijoin it's going to use nested loops, and you can't use an index on a LIKE predicate with % at the beginning. The combination of nested loops + FTS is not pleasant with large volumes of data. Therefore, if this is your situation, you will need to fix the 1NF problem by transforming table1 into normal relational format, and then join it to table2 with an equijoin, which will enable it to use a hash join instead. So:
SELECT table1.col1,
LISTAGG(table2.colB,',') WITHIN GROUP (ORDER BY table2.colB) value_list
FROM (SELECT t.col1,
SUBSTR(t.col2,INSTR(t.col2,',',1,seq)+1,INSTR(t.col2,',',1,seq+1)-(INSTR(t.col2,',',1,seq)+1)) col2_piece
FROM (SELECT col1,
','||col2||',' col2
FROM table1) t,
(SELECT ROWNUM seq FROM dual CONNECT BY LEVEL < 10) x) table1,
table2
WHERE table1.col2_piece IS NOT NULL
AND table1.col2_piece = table2.colA
GROUP BY table1.col1
If you want the values in the same order in the list as the terms then you can use:
SELECT t1.col1,
LISTAGG(t2.colb, ',') WITHIN GROUP (
ORDER BY INSTR(','||t1.col2||',', ','||t2.colA||',')
) AS value2
FROM table1 t1
INNER JOIN table2 t2
ON INSTR(','||t1.col2||',', ','||t2.colA||',') > 0
GROUP BY
t1.col1
Which, for the sample data:
CREATE TABLE Table1 (Col1, Col2) AS
SELECT 'First', 'Code1,Code2,Code3' FROM DUAL UNION ALL
SELECT 'Second', 'Code2' FROM DUAL;
CREATE TABLE Table2 (ColA, ColB) AS
SELECT 'Code1', 'XXXX' FROM DUAL UNION ALL
SELECT 'Code2', 'ZZZZ' FROM DUAL UNION ALL
SELECT 'Code3', 'YYYY' FROM DUAL;
Outputs:
COL1
VALUE2
First
XXXX,ZZZZ,YYYY
Second
ZZZZ
fiddle

SQL sum, grouping by another table

Consider I have a table t1 with a single column a, and it has values, say
a
-
5
10
15
17
I want to write a single SQL query that does the following. Basically, I want to know the sum of all values up to the value in the table t1.
SELECT SUM(value) FROM t2 WHERE value<=5
UNION ALL
SELECT SUM(value) FROM t2 WHERE value<=10
UNION ALL
SELECT SUM(value) FROM t2 WHERE value<=15
UNION ALL
SELECT SUM(value) FROM t2 WHERE value<=17;
If someone changes the value in a, like delete or insert more elements, I have to rewrite the above query. Is there a query that always works automatically?
Here is the DB fiddle link.
I think you appears to want :
SELECT SUM(case when bound<=a[1] then value else 0 end),
. . .
FROM table t;
After edit with fiddle, you can use subquery instead of UNION :
select *, (select sum(t2.value) from t2 where t2.value <= t1.a)
from t1;
I think it's more clear with a join than a subquery, but just personal preference.
SELECT
t1.a,
SUM(COALESCE(t2.value, 0))
FROM
t1
LEFT JOIN
t2
ON
t2.value <= t1.a
GROUP BY
t1.a

How to delete all records returned by a subquery?

I want to delete all records that are returned by a certain query, but I can't figure out a proper way to do this. I tried to DELETE FROM mytable WHERE EXISTS (subquery), however, that deleted all records from the table and not just the ones selected by the subquery.
My subquery looks like this:
SELECT
MAX(columnA) as columnA,
-- 50 other columns
FROM myTable
GROUP BY
-- the 50 other columns above
having count(*) > 1;
This should be easy enough, but my mind is just stuck right now. I'm thankful for any suggestions.
Edit: columnA is not unique (also no other column in that table is globally unique)
Presumably, you want to use in:
DELETE FROM myTable
WHERE columnA IN (SELECT MAX(columnA) as columnA
FROM myTable
GROUP BY -- the 50 other columns above
HAVING count(*) > 1
);
This assumes that columnA is globally unique in the table. Otherwise, you will have to work a bit harder.
DELETE FROM myTable t
WHERE EXISTS (SELECT 1
FROM (SELECT MAX(columnA) as columnA,
col1, col2, . . .
FROM myTable
GROUP BY -- the 50 other columns above
HAVING count(*) > 1
) t2
WHERE t.columnA = t2.columnA AND
t.col1 = t2.col1 AND
t.col2 = t2.col2 AND . . .
);
And, even this isn't guaranteed to work if any of the columns have NULL values (although the conditions can be easily modified to handle this).
Another solution if the uniqueness is only guaranteed by a set of columns:
delete table1 where (col1, col2, ...) in (
select min(col1), col2, ...
from table1
where...
group by col2, ...
)
Null values will be ignored and not deleted.
To achieve this, try something like
with data (id, val1, val2) as
(
select 1, '10', 10 from dual union all
select 2, '20', 21 from dual union all
select 2, null, 21 from dual union all
select 2, '20', null from dual
)
-- map null values in column to a nonexistent value in this column
select * from data d where (d.id, nvl(d.val1, '#<null>')) in
(select dd.id, nvl(dd.val1, '#<null>') from data dd)
If you need to delete all the rows of a table such that the value of a given field is in the result of a query, you can use something like
delete table
my column in ( select column from ...)

remove duplicate values of one column by sql query

I have a table with two column, values of one column is null and another column have many duplicated values and blanks. how can I remove duplicated values and blanks from that column by a query?
You can use temporary table for this task as below:
SELECT DISTINCT * INTO #tmpTable FROM MyTable
TRUNCATE TABLE MyTable
INSERT INTO MyTable SELECT * FROM #tmpTable
You can create an empty copy of the table. Then you run an INSERT INTO new_table SELECT DISTINCT * FROM old_table. Finally, drop the old table and rename the new one.
One way of doing this - using CTE
create table #dups (col1 int, col2 int)
insert into #dups
select null,null union all
select null,1 union all
select null,1 union all
select null,1 union all
select null,2 union all
select null,2 union all
select null,3 union all
select null,null
select * from #dups
;WITH cte
AS (SELECT col1,col2,ROW_NUMBER() OVER (PARTITION BY Col1,Col2
ORDER BY ( SELECT 0)) RN
FROM #dups
)
DELETE FROM cte
WHERE RN > 1 OR col2 is null
1) If you want to keep the row with the lowest id value:
DELETE n1 FROM names n1, names n2 WHERE n1.id > n2.id AND n1.name = n2.name
2) If you want to keep the row with the highest id value:
DELETE n1 FROM names n1, names n2 WHERE n1.id < n2.id AND n1.name = n2.name
Please get the copy of your table before processing this.
if don't get than send me table name i will put replace your table name.
It is tested with MySQL 5.1
Not sure about other versions
see the link
Delete all Duplicate Rows except for One in MySQL?
If I understood right, you want to avoid for the column2 NULL and blanks?
SELECT COLUMN1, COLUMN2 FROM TABLE
GROUP BY
COLUMN1, COLUMN2
WHERE
COLUMN2 NOT NULL AND COULMN2 <> ''
This query is going to show results only when the COLUMN2 has some data.
try this
DELETE FROM [Table]
WHERE (ColmnB IN
(SELECT ColumnB
FROM [Table] AS Table_1
GROUP BY ColumnB
HAVING (COUNT(*) > 1))) OR
(RTRIM(LTRIM(ISNULL(ColumnB,''))) = '')
table has tow column . first column is null value . second column has duplicate value and blank value.

Returning not found values in query

I'm working on a project, and I have a list of several thousand records that I need check. I need to provide the list of the ones NOT found, and provide the query I use to locate them so my superiors can check their work.
I'll admit I'm relatively new to SQL. I don't have access to create temporary tables, which is one way I had thought of to do this.
A basic idea of what I'm doing:
select t.column1, t.column2
from table1 t
where t.column1 in ('value1','value2','value3')
If value1 and value3 are in the database, but value2 is not, I need to display value2 and not the others.
I have tried ISNULL, embedding the query, and trying to select NOT values from the query.
I have searched for returning records not found in a query on Google and on this site, and still found nothing.
I have tried something similar:
First create a table which will contain all such values that you need.
lets say
create table table_values(values varchar2(30));
then try the in clause as below:
select * from table_values tv where tv.value not in (select t.column1
from table1 t);
this will return the values needed.
In SQL Server 2008, you can make derived tables using the syntax VALUES(...)(...)(...), e.g.
select v.value
from (
values ('value1'),('value2'),('value3')
) v(value)
left join table1 t on t.column1 = v.value
where t1.column1 is null
Notes:
Each (...) after VALUES is a single row, and you can have multiple columns.
The v(value) after the derived table is the alias given to the table, and column name(s).
LEFT JOIN keeps the values from the derived table v even when the record doesn't exist in table1
Then, we keep only the records that cannot be matched, i.e. t1.column1 is null
I've switched the first column in your select to the column from v. column2 is removed because it is always null
solution might work in Oracle where dual is single row single column table. You need
one table where you can make virtual select of desired values!
WARNING as I don't have access to db I never tested query below.
SELECT tab_all.col_search, t.column1, t.column2
FROM
(
Select value1 AS col_search from dual
union all
Select value2 from dual
union all
Select value3 from dual
) tab_all left join table1 t
on col_search = t.column1
WHERE t.column1 is null;
I belive sqlserver equivalent of Oracle's
SELECT value1 FROM dual is
SELECT value1 OR SELECT 'value1'.
So try
SELECT tab_all.col_search, t.column1, t.column2
FROM
(
Select value1 AS col_search
union all
Select value2 AS col_search
union all
Select value3 AS col_search
) tab_all left join table1 t
on col_search = t.column1
WHERE t.column1 is null;
As I am not sqlserver person might be that
RichardTheKiwi version of Oracle's select from dual is better.