How to use UNION and order by a specific select? - union

I have two selects:
SELECT id FROM a -- returns 1,4,2,3
UNION
SELECT id FROM b -- returns 2,1
I'm receiving correct num of rows, like: 1,4,2,3
But I want b table results first: 2,1,4,3 or 2,1,4,3

Related

Can't perform an aggregate function on an expression containing an aggregate or a subquery

My table is created like this :
create table ##temp2(min_col1_value varchar (100))
create table ##temp1(max_col1_value varchar (100))
The table has values like this:
min_col1_value
-------------------
1
0
10
1
I'm trying to get the "frequency count of minimum length values" and expecting result as 3.
another example for maximum is :
max_col1_value
-------------------
1000
1234
10
1111
123
2345
I'm trying to get the "frequency count of maximum length values" and expecting result as 4.
When I'm running this query:
select count(min(len(convert(int,min_col1_value)))) from ##temp2 group
by min_col1_value
select count(max(len(convert(int,max_col1_value)))) from ##temp1 group by
max_col1_value
getting error as : Cannot perform an aggregate function on an expression containing an aggregate or a subquery.
How to get the desired result?
You can't aggregate twice in the same SELECT statement and, even if you could, your min(len()) will return a single value: 2 since your minimum field length of #temp2 is 2. Counting that will just give you 1 because there is only 1 value to count.
You are wanting to get the count of how many fields have that minimum length so you'll need something like:
SELECT count(*)
FROM #temp2
WHERE len(min_col1_value) IN (SELECT min(len(min_col1_value)) FROM #temp1)
That WHERE clause says, only count values in #temp2 where the length is equal to the minimum length of all the values in #temp2. This should return 3 based on your sample data.
The same logic can be applied to either table for min or max.
This should get you your desired results:
SELECT COUNT(*)
FROM ##temp2
WHERE LEN(min_col1_value) =
(
SELECT MIN(LEN(min_col1_value))
FROM ##temp2
)
SELECT COUNT(*)
FROM ##temp1
WHERE LEN(max_col1_value) =
(
SELECT MAX(LEN(max_col1_value))
FROM ##temp1
)

T-SQL First_Value() - How can it return more than a single value?

I have the following query:
Select PH.SubId
From dbo.PanelHistory PH
Where
PH.Scribe2Time <> (Select FIRST_VALUE(ReadTimeLocal) OVER (Order By ReadTimeLocal) From dbo.PanelWorkflow Where ProcessNumber = 2690 And dbo.PanelWorkflow.SubId = PH.SubId)
I'm getting an error (512) that says: Subquery returned more than 1 value. This is not permitted when the subquery follows =, !=, <, <= , >, >= or when the subquery is used as an expression.
How can the subquery return more than a single value? There can only be one first value. I must be overlooking something with this query.
By the way, I realize I could easily use Min() instead of First_Value, but I wanted to experiment with some of these Windowing functions.
How many rows do you see?
SELECT FIRST_VALUE(name) OVER (ORDER BY create_date) AS RN
FROM sys.objects
Even though there is only one distinct first value it still returns it for every row in the query.
So if the sub query itself matches multiple rows you will get this error. You could get rid of it with DISTINCT or TOP 1.
Probably not very efficient but you say this is just for experimental purposes.
This isn't an answer. It's just an extended comment generated by the following conclusion:
I could easily use Min() instead of First_Value, but I
wanted to experiment with some of these Windowing functions.
Min can't be used instead of FIRST_VALUE.
Example:
SET NOCOUNT ON;
DECLARE #MyTable TABLE(ID INT, TranDate DATETIME)
INSERT #MyTable VALUES (1, '2012-02-02'), (2, '2011-01-01'), (3, '2013-03-03')
SELECT MIN(ID) AS MIN_ID FROM #MyTable
SELECT ID, MIN(ID) OVER(ORDER BY TranDate) AS MIN_ID_ORDER_BY FROM #MyTable;
SELECT ID, FIRST_VALUE(ID) OVER(ORDER BY TranDate) AS FIRST_VALUE_ID_ORDER_BY FROM #MyTable;
Results:
MIN_ID
-----------
1
ID MIN_ID_ORDER_BY
----------- ---------------
2 2
1 1
3 1
ID FIRST_VALUE_ID_ORDER_BY
----------- -----------------------
2 2
1 2
3 2
FIRST_VALUE() will still return a row for every record that meets tour WHERE clause. TOP 1 should work:
Select PH.SubId
From dbo.PanelHistory PH
Where
PH.Scribe2Time <> (Select TOP 1 ReadTimeLocal
From dbo.PanelWorkflow
Where ProcessNumber = 2690
And dbo.PanelWorkflow.SubId = PH.SubId
Order By ReadTimeLocal DESC)
or MIN:
Select PH.SubId
From dbo.PanelHistory PH
Where
PH.Scribe2Time <> (Select MIN(ReadTimeLocal)
From dbo.PanelWorkflow
Where ProcessNumber = 2690
And dbo.PanelWorkflow.SubId = PH.SubId)
The PARTITION/OVER functions are look-ahead column functions. They aren't row functions - by that, I mean, they don't effect an entire row, number of rows returned, etc. An OVER aggregate can depend on values in other rows, but the tangible result is only to calculate a single column in the current row.
You may have seen something similar to what you are trying to do via an OVER ROW_NUMBER ranking function. Multiple rows are still returned, but only one of them has a ROW_NUMBER of 1. The rest are filtered in an encapsulating WHERE or JOIN predicate.

T-SQL: accessing temporary column in Common Table Expression

Is it possible to access a temporary column that was defined in a query for a Common Table Expression? Say I have
select * from myTable
;with cte as
(
select
*, Salary * 4 as FourTimesSalary
from
Employees
where
Name = #name
and ID >= 100
)
Is there a way to use the temporary column FourTimesSalary when querying cte like so?
select top 2 *
from cte
order by FourTimesSalary, Name
TIA.
Yes you can do that. Example:
with temp as
(
select 1 as id, 2*4 as val
UNION
select 2 as id, 3*4 as val
)
SELECT * FROM temp ORDER BY VAL desc
Your example looks fine, did you get an error when you tried that or something?

SQL Server: Joining in rows via. comma separated field

I'm trying to extract some data from a third party system which uses an SQL Server database. The DB structure looks something like this:
Order
OrderID OrderNumber
1 OX101
2 OX102
OrderItem
OrderItemID OrderID OptionCodes
1 1 12,14,15
2 1 14
3 2 15
Option
OptionID Description
12 Batteries
14 Gift wrap
15 Case
[etc.]
What I want is one row per order item that includes a concatenated field with each option description. So something like this:
OrderItemID OrderNumber Options
1 OX101 Batteries\nGift Wrap\nCase
2 OX101 Gift Wrap
3 OX102 Case
Of course this is complicated by the fact that the options are a comma separated string field instead of a proper lookup table. So I need to split this up by comma in order to join in the options table, and then concat the result back into one field.
At first I tried creating a function which splits out the option data by comma and returns this as a table. Although I was able to join the result of this function with the options table, I wasn't able to pass the OptionCodes column to the function in the join, as it only seemed to work with declared variables or hard-coded values.
Can someone point me in the right direction?
I would use a splitting function (here's an example) to get individual values and keep them in a CTE. Then you can join the CTE to your table called "Option".
SELECT * INTO #Order
FROM (
SELECT 1 OrderID, 'OX101' OrderNumber UNION SELECT 2, 'OX102'
) X;
SELECT * INTO #OrderItem
FROM (
SELECT 1 OrderItemID, 1 OrderID, '12,14,15' OptionCodes
UNION
SELECT 2, 1, '14'
UNION
SELECT 3, 2, '15'
) X;
SELECT * INTO #Option
FROM (
SELECT 12 OptionID, 'Batteries' Description
UNION
SELECT 14, 'Gift Wrap'
UNION
SELECT 15, 'Case'
) X;
WITH N AS (
SELECT I.OrderID, I.OrderItemID, X.items OptionCode
FROM #OrderItem I CROSS APPLY dbo.Split(OptionCodes, ',') X
)
SELECT Q.OrderItemID, Q.OrderNumber,
CONVERT(NVarChar(1000), (
SELECT T.Description + ','
FROM N INNER JOIN #Option T ON N.OptionCode = T.OptionID
WHERE N.OrderItemID = Q.OrderItemID
FOR XML PATH(''))
) Options
FROM (
SELECT N.OrderItemID, O.OrderNumber
FROM #Order O INNER JOIN N ON O.OrderID = N.OrderID
GROUP BY N.OrderItemID, O.OrderNumber) Q
DROP TABLE #Order;
DROP TABLE #OrderItem;
DROP TABLE #Option;

Convert sql subquery to list or varchar

I want to know if we can convert a subquery result to comma separated list in varchar datatype.
For eg.
If I have a product table. And I have product image table with foreign key of product.
now I want to list all product with select query that should have a column with list of productImage table's pk list for each product.
I'm using sql server 2005. Can we achieve the above in any way?
Select p.ProductID,
Stuff((Select ','+Cast(ImageID as varchar(10))
From #ProductImages i
Where p.ProductID=i.ProductId
For XML PATH('')
),1,1,''
) as ImageList
From #Products p
Where p.ProductID in (Select ProductID From #ProductImages)
Here are test data I used for this query
Declare #Products Table (ProductID int primary key, ProductName varchar(20))
Declare #ProductImages Table (ProductId int, ImageId int, Primary Key (ProductId, ImageId))
Insert Into #Products
Select 1, 'Product1' Union all
Select 2, 'Product1' Union all
Select 3, 'Product1' Union all
Select 4, 'Product1' Union all
Select 5, 'Product1'
Insert Into #ProductImages
Select 1,1 Union all
Select 1,2 Union all
Select 1,3 Union all
Select 2,4 Union all
Select 2,5 Union all
Select 3,1 Union all
Select 4,3 Union all
Select 4,5
And here is result of query:
ProductID ImageList
--------- ---------
1 1,2,3
2 4,5
3 1
4 3,5
If you want to have ProductID 5 in the list with null for Image list, just remove next line from query:
Where p.ProductID in (Select ProductID From #ProductImages)
You will have one more row in the result (it does not have images assigned):
5 null
I'm not sure if I'm following you, but maybe you can try to add an extra table:
Table Product
Table Images
Table Product_Images.
In this last table you'll have at least 3 columns, PK for Product_Images table, Product_FK and
Images_FK. Then with just
SELECT Image_FK FROM Product_Images WHERE Product_Images.Product_FK = ##;
you'll have a list of Images' PK associated with a product
Hope this will help you,
regards.
Not easily, no. I've found the best way to do these things is to execute the subquery on its own, and assemble the comma-separated list programmatically.
If you really need to do it on the database, you could define a scalar-valued function that you can give the value for the foreign key, and which returns the comma-separated list. You'll have to use a cursor inside that function to make the magic happen though.

Resources