SQL Custom Order By - sql-server-2008

I can't understand why this doesn't work:
select distinct a.QuestionID,a.QuestionName,b.AnswerID,b.AnswerName
from #TempExportList a
join tblAnswers b
on a.QuestionID = b.QuestionID
where a.PaperID=#PaperID
order by (case when a.QuestionName='A' then 0
when a.QuestionName='B' then 1
else a.QuestionID
end)
I get the following error -
ORDER BY items must appear in the
select list if SELECT DISTINCT is
specified.
But this works fine:
select distinct a.QuestionID,a.QuestionName,b.AnswerID,b.AnswerName
from #TempExportList a
join tblAnswers b
on a.QuestionID = b.QuestionID
where a.PaperID=#PaperID
order by a.QuestionID

The error message explains the problem perfectly.
In the first example the ORDER BY item -- CASE WHEN ... END -- doesn't appear in the SELECT list.
In the second example the ORDER BY item -- a.QuestionID -- does appear in the SELECT list.
To fix the first example you'll need to do something like this:
SELECT DISTINCT a.QuestionID, a.QuestionName, b.AnswerID, b.AnswerName,
CASE WHEN a.QuestionName = 'A' THEN 0
WHEN a.QuestionName = 'B' THEN 1
ELSE a.QuestionID
END
FROM #TempExportList AS a
JOIN tblAnswers AS b
ON a.QuestionID = b.QuestionID
WHERE a.PaperID = #PaperID
ORDER BY CASE WHEN a.QuestionName = 'A' THEN 0
WHEN a.QuestionName = 'B' THEN 1
ELSE a.QuestionID
END

You can get around this with a CTE
;WITH T AS
(
SELECT DISTINCT a.QuestionID,a.QuestionName,b.AnswerID,b.AnswerName
FROM #TempExportList a
JOIN tblAnswers b
ON a.QuestionID = b.QuestionID
WHERE a.PaperID=#PaperID
)
SELECT *
FROM T
ORDER BY
CASE
WHEN QuestionName='A'
THEN 0
WHEN QuestionName='B'
THEN 1
ELSE QuestionID
END

I would have thought the message self explanitory.
You have selected a distinct on a.QuestionID,a.QuestionName,b.AnswerID and b.AnswerName. Therefore, there could be rows of data with the same respective values for each of these fields, but a different one for your case statement.
Consider this
a.QuestionID a.QuestionName b.AnswerID b.AnswerName [case statement]
1 'One' 2 'Two' 0
1 'One' 2 'Two' 1
How does the query know which value in the last column to use in the order? Is it 0? It is 1? Quite simply, it can't determine, so it can't use it, hence the error.
The second example is fine, because a.QuestionID does appear in the SELECT list, and the query can happily apply the ordering.

Related

Get all applications where count of positions more then 1

I have 2 tables
LoanApplications (Id, Name, CreationDate, LoanApplicationStatusId)
Positions(Id, Name, CreationDate, LoanApplicationId)
I need to find all loan applications that have more than 1 position and update LoanApplicationStatusId to 2
I write code to get these LoanApplications like this
SELECT e.Id, count(Name) FROM LoanApplications e
INNER JOIN Positions d ON e.Id=d.LoanApplicationId
GROUP BY e.Id
HAVING COUNT(Name)>1
But I don't understand how to make an update now.
Can you help me?
Straight ahead would be a simple subselect
UPDATE LoanApplications l
SET LoanApplicationStatusId = 2
where (select count(1) from Positions p where p.LoanApplicationId = l.id) > 1
Simply select id of apps which have more than one row, and use it in UPDATE as a condition
UPDATE LoanApplications
JOIN ( SELECT LoanApplicationId
FROM Positions
GROUP BY LoanApplicationId
HAVING COUNT(LoanApplicationId) > 1 ) multi_positional ON id = LoanApplicationId
SET LoanApplicationStatusId = 2
Unsafe query: 'Update' statement without 'where' updates all table rows at once Got this stuff – Eugene Sukh
Convert this query to
UPDATE LoanApplications
JOIN ( SELECT LoanApplicationId
FROM Positions
GROUP BY LoanApplicationId
HAVING COUNT(LoanApplicationId) > 1 ) multi_positional
SET LoanApplicationStatusId = 2
WHERE LoanApplications.id = multi_positional.LoanApplicationId

Should I find correct MySQL query or optimize database structure?

I have database of following structure:
TABLE ingredients (ingredient_name, color)
TABLE recipes (recipe_name)
TABLE recipes_ingredients_parts (recipe_name, ingredient_name, parts)
What I want is to get a recipe that corresponds with selected ingredients and their number. So what I`ve tried first was query:
SELECT rr.* FROM
(SELECT r.* FROM receipes r
INNER JOIN receipes_ingredients_parts ri
ON r.receipe_name = ri.receipe_name
AND ri.ingredient_name = 'espresso'
AND ri.parts_number = '1') rr;
And what I get are {"Americano", "Espresso"}. But that should be "Espresso" only because for "Americano" there should be the query:
SELECT rr.* FROM
(SELECT r.* FROM receipes r
INNER JOIN receipes_ingredients_parts ri
ON r.receipe_name = ri.receipe_name
AND ri.ingredient_name = 'espresso'
AND ri.parts_number = '1') rr
INNER JOIN receipes_ingredients_parts ri
ON rr.receipe_name = ri.receipe_name
AND ri.ingredient_name = 'water'
AND ri.parts_number = '4';
Next my idea was to alter recipe table and add columns for each ingredient to store it's quantity for the recipe. But it would be near 20 columns of that kind. So I'm confused with thought that I'm doing job in a bad style. Maybe I should use some good query for the purpose? Do you guys have any ideas about all the stuff?
I think this is what you are looking for, it should find receipe_names that have all the ingredients in your list, and no other ingredients.
SELECT receipe_name
, SUM(CASE
WHEN (ingredient_name, parts_number) IN (('espresso','1'))
THEN 1 ELSE 0
END
) AS matchedIngredients
, SUM(CASE
WHEN (ingredient_name, parts_number) NOT IN (('espresso','1'))
THEN 1 ELSE 0
END
) AS otherIngredients
FROM receipes_ingredients_parts
GROUP BY receipe_name
HAVING matchedIngredients = 1 AND otherIngredients = 0
A more generalized version/template:
SELECT aField
, SUM(CASE
WHEN someField IN ([matchList])
THEN 1
ELSE 0
END
) AS matches
, SUM(CASE
WHEN someField NOT IN ([matchList])
THEN 1
ELSE 0
END
) AS others
FROM aTable
GROUP BY aField
HAVING matches = [# of values in matchlist]
AND others = 0
Alternatively, if items in the matchlist might be repeated in the table for an "aField" value:
SELECT aField
, COUNT(DISTINCT CASE
WHEN someField IN ([matchList])
THEN someField
ELSE NULL
END
) AS matches
, COUNT(DISTINCT CASE
WHEN someField NOT IN ([matchList])
THEN someField
ELSE NULL
END
) AS others
FROM aTable
GROUP BY aField
HAVING matches = [# of values in matchlist]
AND others = 0

JOINING 2 tables and pass values Into a Third Table

I have 2 tables namely ItemList table and ItemChara table, having this kind of structure:
wherein, Item_Num of Item Chara is a foreign key of Item List table. Now I'm trying to JOIN these tables. With a given like these.
How can I JOIN these tables with those values to form something Like this
I'm trying to find out what is the simplest way to achieve this. I limit the colors in table Item Chara at maximum of 2 colors per Item so it won't exceed 2 colors per item. I want to make another column like Color1 and Color2 to get each color in each item. If it happens that the item doesn't have a color to pair up with, it would just left blank, or null maybe.
Sorry but I'm not sure what to call the thing that I want to do but I know the output that I want to have, so the question title might be irrelevant. I'll change it as soon as I found out.
One way to do this is to use the row_number() window function:
select
i.item_num, i.item_name,
max(case when rn = 1 then ic.chara_color end) color1,
max(case when rn = 2 then ic.chara_color end) color2
from itemlist i
join (
select
item_num, chara_color,
rn = row_number() over (partition by item_num order by chara_num)
from itemchara
) ic on i.item_num = ic.item_num
group by i.item_num, item_name;
Below code will give u the required result.
select A.item_num,A.Item_name,
max(Color_1) AS Color_1,
max(Color_2) As Color_2
from (
select il.item_num,il.Item_name,
(case when ic.chara_num = 1 then ic.chara_color END) AS Color_1,
(case when ic.chara_num = 2 then ic.chara_color END) AS Color_2
from Item_List il inner join Item_Chara ic on il.Item_Num = ic.Item_Num
) A
group by item_num,Item_name
This code is not tested may be have some syntax erro which u need to check and sort it out. Let me know if still ur facing some issue.
Similar to other answers, but since I already made it I might post it anyway.
;with itemlist(itemId, itemName)
as
(
select 1,'Bag'
union ALL
select 2,'Pen'
union ALL
select 3,'Bike'
union ALL
select 4,'Shoes'
)
,itemchara(charaId, itemId, charaColor)
as
(
select 1, 1, 'Blue'
union all
select 2, 1, 'Red'
union all
select 3, 2, 'Black'
union all
select 4, 2, 'Blue'
union all
select 5, 3, 'Green'
union all
select 6, 4, 'Black'
)
,tmp
as
(
select i.itemName
,c.*
,row_number () over ( partition by c.itemId order by c.charaId ) as r
from itemlist i
join itemchara c
on i.itemId = c.itemId
)
select t1. itemId
,t1.itemName
,t1.charaColor
,t2.charaColor
from tmp t1
left join tmp t2
on t1.r+1 = t2.r
and t1.itemId = t2.itemId
where t1.r = 1

Need suggestion formore pretty sql query

I have this query, it does what it is supposed to do.
It finds all records in table 1that have a match in table 2, and assign a pseudo column value 1.
Then it union a query that retreive all records in table 1 that does not have a match in table 2, and assign a pseudo column value 0.
This works, but I am sure it is possible to do it in a more effective way.
SELECT aplac.*, 1 AS selected
FROM aux_placements aplac
JOIN product_placements pplac
ON pplac.placement_id = aplac.placement_id
WHERE pplac.product_id = 1
UNION
SELECT distinct aplac.*, 0 AS selected
FROM aux_placements aplac
WHERE placement_id NOT IN
(SELECT aplac.placement_id
FROM aux_placements aplac
JOIN product_placements pplac
ON pplac.placement_id = aplac.placement_id
WHERE pplac.product_id = 1)
I was thinking something like this might work:
SELECT aplac.*,
CASE WHEN aplac.placement_id IS NULL
THEN 0
ELSE 1
END AS selected
FROM aux_placements aplac
LEFT OUTER JOIN product_placements pplac
ON pplac.placement_id = aplac.placement_id
WHERE pplac.product_id = 1;
Any suggestion is appreciated.
Thanks in advance
EDIT: sqlfiddle is avaliable here: http://sqlfiddle.com/#!2/57013a/1
You want the product id selection to be part of the join condition - change WHERE to AND.
You want to check whether the join found a matching record in the second table - change aplac.placement_id to pplac.placement_id.
So the next query will work for you:
SELECT aplac.*,
CASE WHEN pplac.placement_id IS NULL
THEN 0
ELSE 1
END AS selected
FROM aux_placements aplac
LEFT OUTER JOIN product_placements pplac
ON pplac.placement_id = aplac.placement_id
AND pplac.product_id = 1;

Hibernate HQL - Use CASE WHEN in COUNT Statement like IF in MySQL

I am trying to port a MySQL Query that works to Hibernate HQL, this is all very new to me, so I am open to any kind of hint (Wrong Way, Wrong Structure, change all... ;) )
Two tables A and B. (Structure broken down, only the relevant parts)
A contains entrys, each with a unique ID.
B references those IDs and holds a boolean-like marker (TINYINT(1)).
I want to know how many rows there are in B for each row in A with the Id from A's Row and Marker == True (1).
My MySQL query was like this:
SELECT A.id, COUNT( IF( B.marker = 1, 1, NULL ) ) AS markerTrue, COUNT( IF( B.marker =0, 1, NULL ) ) AS markerFalse FROM A LEFT JOIN B ON B.a_id = A.id GROUP BY A.id
It works and I ported it to this (HQL):
SELECT A.id, COUNT(CASE WHEN B.marker = 1 THEN 1 ELSE NULL END) as markerTrue, COUNT(CASE WHEN B.marker = 0 THEN 1 ELSE NULL END) as markerFalse FROM A LEFT JOIN B WITH B.a_id = A.id GROUP BY A.id
This throws an Exception:
org.hibernate.hql.ast.QuerySyntaxException: unexpected token: CASE near ...
In the logs, there is also
org.hibernate.hql.ast.ErrorCounter - line 1:19: unexpected token: CASE
antlr.NoViableAltException: unexpected token: CASE
But thats just the same internal Error.
Is there a way to do this in HQL? Is there another better way, like restructuring the tables, what is an experts opinion on this?
I am by no means an expert — when HQL stymies me, I rarely have qualms about bypassing the problem by switching to straight SQL — so I can't tell you if there is a better, more HQL-ish way to do this. But in your specific instance, where B.marker is always either 0 or 1, I suppose you could change
COUNT(CASE WHEN B.marker = 1 THEN 1 ELSE NULL END)
to
SUM(B.marker)
and
COUNT(CASE WHEN B.marker = 0 THEN 1 ELSE NULL END)
to
COUNT(*) - SUM(B.marker)
(though you may also need to wrap your SUMs in COALESCE(..., 0) — I'm not sure).
A rewrite in SQL. I hope it's more easily converted to HQL:
SELECT A.id
, COALESCE(markerTrue, 0) AS markerTrue
, COALESCE(markerFalse, 0) AS markerFalse
FROM A
LEFT JOIN
( SELECT a_id
, COUNT(*) AS markerTrue
FROM B
WHERE marker = 1
GROUP BY a_id
) AS BT
ON BT.a_id = A.id
LEFT JOIN
( SELECT a_id
, COUNT(*) AS markerFalse
FROM B
WHERE marker = 0
GROUP BY a_id
) AS BF
ON BF.a_id = A.id
Oops, maybe too late?
Try :
SUM(CASE WHEN B.marker = 0 THEN 1 ELSE NULL END) AS your_result
as there is no "conditionnal" count...