C# EF CORE 관련 질문있습니다!

안녕하세요.
현재 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까지 하여 데이터를 받고 싶은데방법이 없을까요?
열심히 찾아보았지 제 검색 능력이 부족한 건지 비슷한 케이스에 질문도 찾지 못해서 도움을 받고자 질문드립니다.
글 읽어 주셔서 감사합니다.

2개의 좋아요

익명 타입을 사용하지 않고 원하시는 속성을 가진 클래스를 정의한 후 그 클래스로 대체하면 됩니다.

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; }
}
2개의 좋아요
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) 처음과 같은 쿼리 결과가 나옵니다.

늦은 시간까지 답변 주셔서 감사합니다.

2개의 좋아요

저는 아래처럼 테스트를 한 후 답변을 드렸습니다.

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"

원하시는 쿼리와 다를까요?

2개의 좋아요

어쩌면 버젼의 차이일수도 있겠네요. 저는 EF Core 6.0 으로 테스트 하였습니다.

2개의 좋아요

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]

현재 저도 EF Core 6.0 사용 중입니다!

2개의 좋아요

네 그렇네요. 특이한 점은 두번째 쿼리도 U002 가 누락되었네요…

누락된 첫번째 쿼리와 두번째 쿼리의 컬럼 타입이 어떻게 되지요?

2개의 좋아요

현재 두 필드다 INT형 쓰고 있습니다.
String 필드로 테스트해봤을 때도 똑같이 누락되어서 나왔습니다!

2개의 좋아요

혹시 재현 가능한 코드를 공유 줄 수 있을까요?

2개의 좋아요

서버에 연결해서 공부하는 중이라 재현 할 수 있는 코드가 없습니다. 죄송합니다. 제 생각으로 아무 쿼리에 select { ~~~ . A001 = 0 , A002 = 0 } 뒤쪽이나 앞쪽에 고정으로 겹치는 값으로 필드를 생성해 주시면 똑같이 쿼리가 누락되어 나 올 것 같습니다!

2개의 좋아요

키가 없는 테이블 맞나요?

2개의 좋아요

제가 컴퓨터가 아니어서 잘 생각나지 않지만 둘 다 A001 B001에 키가 잡혀있을 것 같습니다.

정말 죄송하지만 제가 잠이 너무 와서 다음 답글은 일어난 후 최대한 빨리 드리겠습니다.
늦은 시간 댓글 달아 주시고 제 질문에 같이 고민해 주셔서 감사합니다.

3개의 좋아요

재현이 되었습니다. 흠… 이거 정상적인 동작이 아닌 것 같은데요, DB는 sqlite고요

패턴은,

값이 0 또는 1등 모두 같으면, 심지어 U001 ~ U004 가 모두 0일 때 U001 만 0 이고 U002 ~ U004는 누락되네요…

그래서 U001 = 0, U002 = 1 이런 식으로 하면 누락되지 않습니다.

3개의 좋아요

중복되지 않는 값을 넣음 누락되지 않는다는 점은 여러 테스트를 해봐 알고 있었습니다. 하지만 목표 쿼리가 Union => Group By => Sum을 하는 게 목적이기 때문에 0 이 외의 값을 넣기가 어려워 질문 글을 올리게 되었습니다.
또한 개인적인 궁금증으로 왜 중복 값을 넣으면 값이 없어지는지 이유가 궁금하기도 했습니다.

3개의 좋아요

버그인지 EF Core의 일종의 최적화? 인지 저도 좀 알아볼께요. 만약 버그라면 DB 구현체에 따라 다른 결과가 나올것 같고요 만약 최적화 관련된 것이라면 관련 옵션이 있을 것이라 생각합니다.

3개의 좋아요

밤늦게부터 지금까지 답변해 주셔서 감사드립니다.
저도 열심히 찾아보겠습니다.
좋은 하루 보내세요.

2개의 좋아요

확인한 내용 (진행중…)

EF 6 및 EF 7 프리뷰에서 확인했고 sqlite 및 progresql에서 확인했는데 모두 결과가 동일합니다. ㅡ.,ㅡ


최종 결과 값에는 적용한 리터럴 값이 제대로 있는 것으로 보아 버그는 아닌 것 같고 일종의 EF Core의 최적화가 도작한 것 같네요. 아마 관련 옵션을 찾을 수 있으면 될 듯 합니다.

2개의 좋아요

결론 - 버그아님.

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"
3개의 좋아요

Int, Double, Float 등은 문제없이 Union 되는 걸 확인했습니다!

제 코드에서 ‘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 안되는 이유는 제가 한번 혼자서 알아보도록 하겠습니다.
지금까지 답변 감사합니다.

4개의 좋아요