안녕하세요.
현재 EF CORE 공부하고 있습니다.
하나 이해가 되지 않는 부분이 있어 이렇게 글을 작성하게 되었습니다.
현재 제가 원하는 동작은 A 와 B 테이블의 Union입니다.
Union 을 하기 위해 칼럼 개수와 칼럼 타입을 맞추는 작업을 하다가 생긴 일입니다.
간단하게 설명하자면
A Column A001, A002, A003, A004
B Column B001, B002, B003, B004
A는 A001, A002 만 필요하고 B는 B003, B004 만 필요하여 아래 코드처럼 작성하여 작성했습니다.
var aTable = A.Select(s => new {U001 = s.A001, U002 = s.A002 , U003 = 0 , U004 = 0});
var bTable = B.Select(s => new {U001 = 0, U002 = 0 , U003 = s.B003 , U004 = s.B004});
var uTable = aTable.Concat(bTable);
컴파일 상 오류는 없지만 실행을 하게 되면
‘Unable to translate set operation when matching columns on both sides have different store types’
라는 오류가 발생합니다.
그래서 ef core에서 생성되는 쿼리를 살펴보면
SELECT [v].[A001] AS [U001], [v].[A002] AS [U002], 0 AS [U003]
FROM [A] AS [v]
SELECT 0 AS [U001], [v].[B003] AS [U003], [v].[B004] AS [U004]
FROM [B] AS [v]
처럼 쿼리가 생성됩니다.
제가 지정해둔 필드가 쿼리로 생성되지 않는 것을 발견했습니다.
ToList를 이용해서 불러온 뒤 Concat를 하면 잘 작동하지만 서버에서 쿼리로 Union까지 하여 데이터를 받고 싶은데방법이 없을까요?
열심히 찾아보았지 제 검색 능력이 부족한 건지 비슷한 케이스에 질문도 찾지 못해서 도움을 받고자 질문드립니다.
글 읽어 주셔서 감사합니다.
익명 타입을 사용하지 않고 원하시는 속성을 가진 클래스를 정의한 후 그 클래스로 대체하면 됩니다.
var aTable = A.Select(s => new Info {U001 = s.A001, U002 = s.A002 , U003 = 0 , U004 = 0});
var bTable = B.Select(s => new Info {U001 = 0, U002 = 0 , U003 = s.B003 , U004 = s.B004});
var uTable = aTable.Concat(bTable);
public class Info
{
public int U001 { get; set; }
public int U002 { get; set; }
public int U003 { get; set; }
public int U004 { get; set; }
}
public class info
{
public int U001 { get; set; }
public int U002 { get; set; }
public int U003 { get; set; }
public int U004 { get; set; }
}
B.Select(s => new info{ U001 = 0, U002 = 0 , U003 = s.B003 , U004 = s.B004}).ToQueryString();
SELECT 0 AS [U001], [v].[C003] AS [U003], [v].[B004] AS [B004] FROM [B] AS [v]
말씀해 주신 방법처럼 클래스를 만들어 실행해 보았지만 (ToQueryString) 처음과 같은 쿼리 결과가 나옵니다.
var a = c.Users.Where(x => x.UserId == "test").Select(x => new Temp { A = x.UserId, B = x.UserName, C = "A" });
var b = c.Todos.Select(x => new Temp { A = x.Memo, B = x.UserId, C = "B" });
var ab = a.Concat(b);
class Temp
{
public string A { get; set; }
public string B { get; set; }
public string C { get; set; }
}
| 생성 쿼리
SELECT "u"."UserId" AS "A", "u"."UserName" AS "B", 'A' AS "C"
FROM "UserInfo" AS "u"
WHERE "u"."UserId" = 'test'
UNION ALL
SELECT "t0"."Memo" AS "A", "t0"."UserId" AS "B", 'B' AS "C"
FROM "TodoInfo" AS "t0"
A.Select(s => new Info {U001 = s.A001, U002 = s.A002 , U003 = 0 , U004 = 0}).ToQueryString();
SELECT를 요청했을 때
[v].[A001] AS [U001], [v].[A002] AS [U002], 0 AS [U003] , 0 AS [U004] 이 아닌
[v].[A001] AS [U001], [v].[A002] AS [U002], 0 AS [U003] 쿼리가 만들어집니다.
U004 Column이 생성되지 않아 질문드렸습니다!
현재 쿼리:
SELECT [v].[A001] AS [U001], [v].[A002] AS [U002], 0 AS [U003]
FROM [A] AS [v]
원하는 쿼리:
SELECT [v].[A001] AS [U001], [v].[A002] AS [U002], 0 AS [U003] , 0 AS [U004]
FROM [A] AS [v]
서버에 연결해서 공부하는 중이라 재현 할 수 있는 코드가 없습니다. 죄송합니다. 제 생각으로 아무 쿼리에 select { ~~~ . A001 = 0 , A002 = 0 } 뒤쪽이나 앞쪽에 고정으로 겹치는 값으로 필드를 생성해 주시면 똑같이 쿼리가 누락되어 나 올 것 같습니다!
중복되지 않는 값을 넣음 누락되지 않는다는 점은 여러 테스트를 해봐 알고 있었습니다. 하지만 목표 쿼리가 Union => Group By => Sum을 하는 게 목적이기 때문에 0 이 외의 값을 넣기가 어려워 질문 글을 올리게 되었습니다.
또한 개인적인 궁금증으로 왜 중복 값을 넣으면 값이 없어지는지 이유가 궁금하기도 했습니다.
Select에 의해 지정된 리터럴 값의 속성이 쿼리에서 탈락 되는 것은 버그가 아니라 EF Core의 최적화인 것으로 보입니다.
생성된 쿼리와 상관없이 결과에는 제대로 저장됩니다.
그런데 문제는 union all 연산을 할 수 없다는 점인데요. 그래서 다시 돌아와서 @외톨이 님의 관련 질의를 했는데, 제대로 질의되는 것으로 확인됩니다.
var a = c.ATable.Select(x => new Info { U001 = 0, U002 = 0, U003 = x.A003, U004 = x.A004 });
var b = c.BTable.Select(x => new Info { U001 = x.B001, U002 = x.B002, U003 = 0, U004 = 0 });
var ac = a.Concat(b).ToArray();
class Info
{
public int U001 { get; set; }
public int U002 { get; set; }
public int U003 { get; set; }
public int U004 { get; set; }
}
| 생성 쿼리
SELECT 0 AS "U001", 0 AS "U002", "a"."A003" AS "U003", "a"."A004" AS "U004"
FROM "ATable" AS "a"
UNION ALL
SELECT "b"."B001" AS "U001", "b"."B002" AS "U002", 0 AS "U003", 0 AS "U004"
FROM "BTable" AS "b"
제 코드에서 ‘Unable to translate set operation when matching columns on both sides have different store types’ 이 오류가 나오는 이유가 decimal 타입이 있어서 발생한 오류였습니다.
Ex) A008 (decimal) A.Select(s => s.A008 ).Union(A.Select(s =>0.0M)).ToQueryString();
(decimal 타입하고 0.0M (Convert.ToDecimal, decimal.Parse (decimal) 포함) 고정 값이 Union 이 되지 않습니다. Double, Float으로 캐스팅 후 Union 하면 잘 되지만 decimal 타입을 유지하면서 Union 은 되지 않습니다.)
decimal 이 Union 안되는 이유는 제가 한번 혼자서 알아보도록 하겠습니다.
지금까지 답변 감사합니다.