Does SQL "Reuse" results from a WITH clause? - sql-server-2008

I have a query that has something like this:
;with Dataset1 as (SELECT columnA, columnB, SUM(columnC) as columnC FROM Table1 GROUP BY columnA, columnB )
SELECT
(SELECT columnC FROM Dataset1 WHERE columnA = '1' and columnB='2') as Result1,
(SELECT columnC FROM Dataset1 WHERE columnA = '2' and columnB='3') as Result2,
(SELECT columnC FROM Dataset1 WHERE columnA = '3' and columnB='4') as Result3,
...
What I'm wondering is does Dataset1 get selected in-memory and reused with every one of those select clauses, or does SQL fetch it each time? If that's the case, that seems really wasteful, and what should I do to optimize this ?

Related

How to correctly convert SQL rows to columns?

Before someone mentions it, I have seen the exact same SQL question on Stack before and from my point of view, that's actually transposing columns to rows. What I'm trying to accomplish is as seen below in the photos, given the top one, I want to create a new table with the previous data that is flipped in this sense.
You can use the conditional aggregation and union all as follows:
select name_new,
max(case when name = 'PersonA' then A end) as PersonA,
max(case when name = 'PersonB' then A end) as PersonB,
max(case when name = 'PersonC' then A end) as PersonC
from
(select name, 'A1' name_new, A1 A from mytable union all
select name, 'A2' name_new, A2 A from mytable union all
select name, 'A3' name_new, A3 A from mytable union all
select name, 'A4' name_new, A4 A from mytable ) t
group by name_new
SQLFiddle

Select where value difference between (columnA and columnB) > 0,10

I want to compare two columns. I want select only column where value difference between two columns > 0,10.
Example:
Select ColumnA from Table1 where CoLumnA <> ColumnB > 0,10
SELECT ColumnA
FROM Table1
WHERE ColumnA <> ColumnB
AND ABS(ColumnA - ColumnB) BETWEEN 0 AND 10
Details on ABS() and BETWEEN can be found here:
ABS()
BETWEEN

Sql distinct and group by boolean field

I want to count the distinct occurrences of some column grouped and non-grouped by two boolean columns
select count(Distinct some_column) as Uniques, sum(some_other_col)
from myTable T
where T.created_at>'2016-09-15'
group by T.col1, T.col2
This query gives four values
uniques when col1=true and col2=true
uniques when col1=true and col2=false
uniques when col1=false and col2=true
uniques when col1=false and col2=false
Is it possible to change the same query and to get these three values?
I can't get that info combining the first 4 values
uniques (all)
uniques when col1=true
uniques when col2=true
UPDATE
Actually I want to keep the group by because there are some other values that I get the sum.
Use conditional aggregation:
select count(Distinct some_column) as Uniques,
count(distinct case when t.col1 then some_column end) as Uniques_col1_true,
count(distinct case when t.col2 then some_column end) as Uniques_col2_true
from myTable t
where t.created_at > '2016-09-15';
Try this
SELECT SUM(CASE WHEN col1 = TRUE
AND col2 = TRUE THEN 1 ELSE 0 END) AS opt1,
SUM(CASE WHEN col1 = TRUE
AND col2 = FALSE THEN 1 ELSE 0 END) AS opt2,
FROM
(SELECT DISTINCT col1,
col2,
somecolumn
FROM TABLE T
WHERE ...) AS TB

Combining select distinct and order by

This is my SQL query :
SELECT DISTINCT dest
FROM messages
WHERE exp = ?
UNION
SELECT DISTINCT exp
FROM messages
WHERE dest = ?
With this query, I get all the messages I've sent or received. But in my table messages, i have a field timestamp, and i need, with this query, add an order by timestamp... but how ?
You can do this without a union:
SELECT (case when exp = ? then dest else exp end), timestamp
FROM messages
WHERE exp = ? or dest = ?;
Then to get the most recent message for each participant, use group by not distinct:
SELECT (case when exp = ? then dest else exp end) as other, max(timestamp)
FROM messages
WHERE exp = ? or dest = ?
group by (case when exp = ? then dest else exp end)
order by max(timestamp) desc;
SELECT *
FROM
( SELECT
col1 = dest,
col2 = MAX(timestampCol)
FROM
messages
WHERE
exp = ?
GROUP BY
dest
UNION
SELECT
col1 = exp,
col2 = MAX(timestampCol)
FROM
messages
WHERE
dest= ?
GROUP BY
exp
) tbl
ORDER BY col2
This should return only one row per distinct exp / dest though I'm sure this could probably be done without a union; The GROUP BY will only get the most recent one.
Updated SQL: Given that it is possible for an exp on one record to equal a dest on the same or another record.
SELECT
CASE WHEN exp = ? THEN dest ELSE exp END AS col1
,MAX(timestampCol) AS col2
FROM
messages
WHERE
exp = ?
OR dest = ?
GROUP BY
(CASE WHEN exp = ? THEN dest ELSE exp END)
ORDER BY
MAX(timestampCol) DESC;
You might want to consider adding an SQL Fiddle with some dummy data to allow users to better help you.
SELECT *
FROM
(
SELECT DISTINCT dest AS Column1, timestamp AS Column2
FROM messages
WHERE exp = ?
UNION
SELECT DISTINCT exp AS Column1, timestamp AS Column2
FROM messages
WHERE dest = ?
) tbl
ORDER BY Column2
This query will help you in getting records order by timestamp
SELECT tbl.mailId, Max(timestampColumn) MostRecentDate
FROM
(
SELECT exp AS mailId, Max(timestampCol) AS timestampColumn
FROM Employeeatd
WHERE dest = ?
GROUP BY exp
UNION
SELECT Dept AS abc, Max(timestampCol) AS timestampColumn
FROM Employeeatd
WHERE exp = ?
GROUP BY Dept
) tbl
GROUP BY mailId
ORDER BY Max(timestampColumn) desc

temporary table has a count for each other table

I'm trying to make a statistics page in my php script. in order to select the count from each table I need more than 30 Queries like this
SELECT COUNT(order_id) as `uncompleted_orders` FROM `orders` WHERE `order_status` != 0
and then I need to run another query like this:
SELECT COUNT(order_id) as `completed_orders` FROM `orders` WHERE `order_status` = 1
I've tried this approach, but it didn't work:
SELECT COUNT(order_id) as `uncompleted_orders` FROM `sd_orders` WHERE `order_status` != 4;
SELECT COUNT(order_id) as `completed_orders` FROM `sd_orders` WHERE `order_status` = 4;
Is there any way to creat a new temp table in MySQL contains the count for other tables?
You could try something like this:
SELECT
(
SELECT COUNT(order_id) FROM `sd_orders` WHERE `order_status` != 4
) as `uncompleted_orders`,
(
SELECT COUNT(order_id) FROM `sd_orders` WHERE `order_status` = 4
) as `completed_orders`
You will have a result set with one row and a field for each count.
Without more information it's impossible to generalise, but there are many constructs that can help you here.
First, your example is actually from one table, and not two. This means that you can do the following...
SELECT
COUNT(CASE WHEN order_status = 4 THEN order_id END) AS complete_orders,
COUNT(CASE WHEN order_status <> 4 THEN order_id END) AS incomplete_orders
FROM
sd_orders
This works because COUNT(<something>) doesn't include an NULLs in the results. And by not including an ELSE clause, anything that doesn't match returns NULL. Another way people accomplish the same result is SUM(CASE WHEN ? THEN 1 ELSE 0 END).
Second, where you do actually have multiple tables, you can combine the results in several different ways...
-- Where you want one value from each table...
--------------------------------------------------------------------------------
SELECT
(SELECT COUNT(*) FROM table1 WHERE fieldx = ?) AS value1,
(SELECT COUNT(*) FROM table2 WHERE fieldy = ?) AS value2
-- Where you want one row of values from each table...
--------------------------------------------------------------------------------
SELECT
table1_summary.value1 AS table1_value1,
table1_summary.value2 AS table1_value2,
table2_summary.value1 AS table2_value1,
table2_summary.value2 AS table2_value2
FROM
(
SELECT
COUNT(CASE WHEN fieldx = ? THEN id END) AS value1,
COUNT(CASE WHEN fieldx <> ? THEN id END) AS value2
FROM
table1
)
AS table1_summary
CROSS JOIN
(
SELECT
COUNT(CASE WHEN fieldy = ? THEN id END) AS value1,
COUNT(CASE WHEN fieldy <> ? THEN id END) AS value2
FROM
table2
)
AS table2_summary
-- Where you want many rows, but of the same fields, from each table...
--------------------------------------------------------------------------------
SELECT
*
FROM
(
SELECT
'Table1' AS source_table,
fielda AS some_grouping,
COUNT(CASE WHEN fieldx = ? THEN id END) AS value1,
COUNT(CASE WHEN fieldx <> ? THEN id END) AS value2
FROM
table1
GROUP BY
fielda
UNION ALL
SELECT
'Table2' AS source_table,
fieldb AS some_grouping,
COUNT(CASE WHEN fieldy = ? THEN id END) AS value1,
COUNT(CASE WHEN fieldy <> ? THEN id END) AS value2
FROM
table2
GROUP BY
fieldb
)
AS summary
ORDER BY
source_table,
some_grouping,
value1,
value2
As you can see, there are a lot of ways to do this. How you approach it totally depends on your data and your needs.