Sorry, I asked this question just before and got some good answers but then I realised I made a mistake with the query in question, if I change the question in the original post that could make the answers invalid so I'm posting again with the right query this time, please forgive me, I hope this is acceptable.
DECLARE #Temp TABLE
(MeasureDate, col1, col2, type)
INSERT INTO #Temp
SELECT MeasureDate, col1, col2, 1
FROM Table1
WHERE Col3 = 1
INSERT INTO #Temp
SELECT MeasureDate, col1, col2, 3
FROM Table1
WHERE Col3 = 1
AND Col4 = 7000
SELECT SUM(col1) / SUM(col2) AS Percentage, MeasureDate, Type
FROM #Temp
GROUP BY MeasureDate, Type
I do two inserts into the temp table, 2nd insert with an extra WHERE but same columns same table, but different type, then I do SUM(col1) / SUM(col2) on the temp table to return the result I need per MeasureDate and type. Is there a way to merge all these inserts and selects into one statement so I don't use a temp table and do a single select from Table1? Or even if I still need the temp table, merge the selects into one select instead of two separate selects? Stored procedure works fine as it is, just looking for a way to shorten it.
Thanks.
Sure can. I might start with combining the two queries from your inserts using UNION ALL (this variation of UNION will not remove duplicates), wrapped up in a CTE from which you can perform your final query:
WITH MeasureData(MeasureDate, col1, col2, type) AS (
SELECT MeasureDate, col1, col2, 1
FROM Table1
WHERE Col3 = 1
UNION ALL
SELECT MeasureDate, col1, col2, 3
FROM Table1
WHERE Col3 = 1
AND Col4 = 7000
)
SELECT SUM(col1) / SUM(col2) AS Percentage, MeasureDate, Type
FROM MeasureData
GROUP BY MeasureDate, Type
That's it, no more table variable or insert statements.
No real need for a UNION, you can handle this with a CASE statement:
SELECT SUM(col1) / SUM(col2) AS Percentage, MeasureDate, Type
FROM (
SELECT MeasureDate, col1, col2, case when Col4 = 7000 then 3 else 1 end type
FROM Table1
WHERE Col3 = 1
) t
GROUP BY MeasureDate, Type
Edit, as Gordon correctly points out, for Type = 1, this query wouldn't produce the same results. Here's a variation on Gordon's good answer that might be easier to visually understand using a CROSS JOIN and IF logic:
SELECT T1.MeasureDate,
T.Type,
SUM(IF(T.Type=1,Col1,IF(T.Type=3 AND T1.Col4=7000,T1.Col1,0))) /
SUM(IF(T.Type=1,Col2,IF(T.Type=3 AND T1.Col4=7000,T1.Col2,0))) AS Percentage
FROM Table1 T1
CROSS JOIN (SELECT 1 Type UNION SELECT 3) T
WHERE T1.Col3 = 1
GROUP BY T1.MeasureDate, T.Type
Condensed SQL Fiddle
Your method is double counting cases where col3 = 1 and col4 = 7000. Here is a method that takes this into account, without union on the overall table:
select t.type, SUM(t1.col1) / SUM(t1.col2) AS Percentage, t1.MeasureDate, t.Type
from table1 t1 join
(select 1 as type union all
select 3 as type
) t
on t.type = 1 or t1.col4 = 7000
where t1.col3 = 1
group by measuredate, type;
Related
I am using a platform which accepts minimal SQL functions to write a SQL code. The UNPIVOT function cannot be used on the platform so I have to do this manually. I am thinking along the line of UNION ALL and then CROSS JOINING (which I attempted but ended up with the wrong record counts. Please see image attached.
Any help / pointer will be highly appreciated!
I don't know how you used UNION ALL but it can be done like this:
select col1, col2, col3 as NewCol from Table1
union all
select col1, col2, col4 from Table1
You could also use an ORDER BY clause, so that rows with the same col1 and col2 appear in subsequent rows:
select col1, col2, NewCol
from (
select col1, col2, col3 as NewCol, 1 as ord from Table1
union all
select col1, col2, col4, 2 from Table1
) t
order by col1, col2, ord
A portable approach uses union all:
select col1, col2, col3 as newcol from mytable
union all
select col1, col2, col4 from mytable
If your database supports lateral joins (also called cross apply in some databases) and values(), this can be simplified:
select t.col1, t.col2, x.newcol
from mytable t
cross join lateral (values(col3), (col4)) x(newcol)
You can use a cross join, but it requires some case logic. The exact syntax depends on the database, but something like this:
select t.col1, t.col2,
(case when n.n = 1 then t.col3 else t.col4 end) as newcol
from t cross join
(select 1 as n union all select 2) n;
To load another table, you would do one of the following:
insert these results into a table that has already been created.
Use select into or create table as depending on the database.
If you care about the ordering, then you can add order by t.col1, t.col2, n.n.
In most cases, a simple union all approach is fine (such as GMB suggests). That approach requires scanning the table twice, which incurs some additional overhead. However, if the "table" is really a complex query or view, then only processing it once is a bigger advantage.
I'm trying to write a query that pulls data from a lot of tables, and has about 20 unions. It's pulling the same information repeatedly, but with more layers each time, to show a sort of tree.
I want to compare the final two columns. I'm using a case to do this, and if I add a case to this query then I get the error "query block has incorrect number of results columns". This seems to be because the final select in the union has an extra column (the compare case).
Is there any way to work around this? I don't want to add the case to each select, as this would add about 15 more columns that I don't want.
Use a sub-query:
SELECT col1,
col2,
CASE
WHEN col1 = 'somevalue'
THEN 'someresult'
ELSE 'otherresult'
END AS col3
FROM (
SELECT col1, col2 FROM table1 UNION ALL
SELECT col1, col2 FROM table2 UNION ALL
SELECT col1, col2 FROM table3
-- ...
);
Or use a sub-query factoring clause:
WITH data ( col1, col2 ) AS (
SELECT col1, col2 FROM table1 UNION ALL
SELECT col1, col2 FROM table2 UNION ALL
SELECT col1, col2 FROM table3
-- ...
)
SELECT col1,
col2,
CASE
WHEN col1 = 'somevalue'
THEN 'someresult'
ELSE 'otherresult'
END AS col3
FROM data;
I have getting same records multiple times with one column value changed using union like this.
Select col1, col2, 'A' as col3
Union
Select col1, col2, 'B' as col3
Union
Select col1, col2, 'C' as col3
I want to know if there is any way to do this in single query instead of writing 3 queries. Can someone please help?
I simple CROSS JOIN with the specified VALUES should do the trick
Select Col1,Col2,Col3
From YourTable A
Cross Join (Values ('A'),('B'),('C') ) B (Col3)
SELECT d.name, x.field1
FROM sys.databases d
, (VALUES('A'), ('B'), ('C')) AS x(field1)
You should look up the cross join syntax but this is an example off how to do it.
I have 2 SQL's and the result come fine. They are no relation between those 2 queries but I want to see all the rows in single column.
e.g.
Select col1,col2,sum(col3) as col3 from table a
select col4,col5 from table b
I would like the result to be
col1 col2 col3 col4 col5
If there is no equivalent row for either table a or table b replace with zeroes.
Could some one help me with this. thanks.
Since, you didn't provided any information like table structure or data inside each tables. You can cross join both tables.
select t.col1,t.col2,t.col3,t1.col1,t1.col2 from tab1 t,tab2 t1;
SQLFiddle
In both select statements add column based on rownum or row_number() and then full join results using this column:
select nvl(col1, 0) col1, nvl(col2, 0) col2, nvl(col3, 0) col3,
nvl(col4, 0) col4, nvl(col5, 0) col5
from
(select rownum rn, col1, col2, col3 from (
select col1, col2, sum(col3) col3 from tableA group by col1, col2)) a
full join (select rownum rn, col4, col5 from tableB) b using (rn)
SQLFiddle demo
I guess a UNION could be a pragmatic solution since the 2 queries are not related. They are just 2 data sets that should be retrieved in one statement:
Select col1,col2,sum(col3) as col3 from table a
UNION
select col4,col5, to_number(null) col6 from table b
Be aware of col6 in the example. SQL insists on retrieving an equal set of columns in a UNION statement. It is a good practice to retrieve columns with exactly the same datatype. Since the sum(col3) will yield a number datatype column, col6 should too.
The outcome of col4 and col5 will be shown in col1 and col2.
I think I have a misunderstanding of how NOT EXISTS work and hope it can be clarified to me.
Here is the sample code I am running (also on SQL Fiddle)
select sum(col1) col1, sum(col2) col1, sum(col3) col3
from (
select 1 col1, 1 col2, 1 col3
from dual tbl1
)
where not exists(
select 2 col1, 1 col2, 1 col3
from dual tbl2
)
I thought that it should return:
1, 1, 1
But instead it returns nothing.
I make this assumption only on the fact that I though NOT EXISTS would give me a list of all the rows in the first query that do not exist in the second query (in this case 1,1,1)
Why does this not work
What would be the appropriate way to make it work the way I am expecting it to?
You are performing an uncorrelated subquery in your NOT EXISTS() condition. It always returns exactly one row, therefore the NOT EXISTS condition is never satisfied, and your query returns zero rows.
Oracle has a rowset difference operator, MINUS, that should do what you wanted:
select sum(col1) col1, sum(col2) col1, sum(col3) col3
from (
select 1 col1, 1 col2, 1 col3
from dual tbl1
MINUS
select 2 col1, 1 col2, 1 col3
from dual tbl2
)
SQL Server has an EXCEPT operator that does the same thing as Oracle's MINUS. Some other databases implement one or the other of these.
EXISTS just returns true if a record exists in the result set; it does not do any value checking. Since the sub-query returns one record, EXISTS is true, NOT EXISTS is false, and you get no records in your result.
Typically you have a WHERE cluase in the sub-query to compare values to the outer query.
One way to accomplish what you want is to use EXCEPT:
select sum(col1) col1, sum(col2) col1, sum(col3) col3
from (
select 1 col1, 1 col2, 1 col3
from dual tbl1
)
EXCEPT(
select 2 col1, 1 col2, 1 col3
from dual tbl2
)
A not exists that includes a select from dual will never return anything. Not exists will exclude rows where the embedded SQL returns something. Normally not exists should be used more like this:
select ... from MY_TABLE A where not exists (select 1 from OTHER_TABLE B where A.SOME_COL = B.SOME_COL)
As using NOT EXISTS is not good approach as it is return only single row so try it with MINUS or EXCEPT
select sum(col1) col1, sum(col2) col1, sum(col3) col3 from ( select 1 col1, 1 col2, 1 col3 from dual tbl1 MINUS select 2 col1, 1 col2, 1 col3 from dual tbl2 )
select sum(col1) col1, sum(col2) col1, sum(col3) col3 from ( select 1 col1, 1 col2, 1 col3 from dual tbl1 ) EXCEPT( select 2 col1, 1 col2, 1 col3 from dual tbl2 )