데이터베이스 작업을 하다 보면, 종종 여러 행에 있는 데이터를 하나의 문자열로 결합하여 표시해야 할 때가 있습니다. 예를 들어, 동일한 그룹의 모든 항목 이름을 하나의 열에 표시하고 싶을 때가 있습니다. MySQL에서 이 작업을 쉽게 수행할 수 있는 방법은 바로 GROUP_CONCAT 함수를 사용하는 것입니다.

GROUP_CONCAT 함수란?

GROUP_CONCAT 함수는 MySQL에서 제공하는 집계 함수 중 하나로, 그룹화된 데이터에서 여러 값을 하나의 문자열로 결합해 줍니다. 이 함수는 GROUP BY와 함께 사용되며, 여러 행에 걸쳐 있는 값을 하나의 결과로 만들어 줍니다. 결합된 값들은 기본적으로 쉼표(,)로 구분되지만, 다른 구분자를 사용할 수도 있습니다.

기본 사용법

GROUP_CONCAT 함수의 기본 구문은 다음과 같습니다:

GROUP_CONCAT(
  [DISTINCT] column_name 
  [ORDER BY column_name ASC|DESC] 
  [SEPARATOR 'separator_string']
  )
  • DISTINCT: 중복된 값을 제거하고 싶을 때 사용합니다.
  • ORDER BY: 결합할 값을 정렬할 때 사용합니다. 기본적으로 오름차순(ASC)으로 정렬됩니다.
  • SEPARATOR: 결합된 값 사이에 사용할 구분자를 지정합니다. 기본값은 쉼표(,)입니다.

실습 예제: 클래스 이름 결합하기

이제 실제 예제를 통해 GROUP_CONCAT의 사용법을 살펴보겠습니다. 예를 들어, 특정 회사에서 제공하는 워크아웃 클래스의 이름을 하나의 열에 모아서 표시하고 싶다고 가정해 보겠습니다.

SELECT 
  ca.goods_id, 
  GROUP_CONCAT(DISTINCT wc.name ORDER BY wc.name SEPARATOR ', ') AS class_names 
FROM class_authority ca 
LEFT JOIN workout_class wc ON wc.id = ca.class_id 
WHERE wc.company_id = 296 
GROUP BY ca.goods_id;

쿼리 설명

  • ca.goods_id: 클래스와 연결된 상품 ID입니다. 이 필드를 기준으로 그룹화하여 각 상품에 대한 결과를 하나의 행으로 표시합니다.
  • GROUP_CONCAT(DISTINCT wc.name ORDER BY wc.name SEPARATOR ', '): 동일한 goods_id에 속하는 클래스 이름(wc.name)을 중복 제거 후 알파벳 순서대로 정렬하여 쉼표로 구분된 문자열로 결합합니다.
  • LEFT JOIN workout_class wc ON wc.id = ca.class_id: class_authority 테이블과 workout_class 테이블을 조인하여 각 상품 ID에 해당하는 클래스를 가져옵니다.
  • WHERE wc.company_id = 296: 특정 회사(예: company_id = 296)에 속하는 클래스만 선택합니다.
  • GROUP BY ca.goods_id: goods_id를 기준으로 결과를 그룹화하여, 각 상품에 대해 하나의 행만 반환합니다.

결과 예시

위 쿼리를 실행하면 다음과 같은 결과를 얻을 수 있습니다:

101 Yoga, Zumba, Pilates
102 Body Pump, CrossFit, Spin
103 Kickboxing, Muay Thai, Karate

여기서 goods_id = 101에 대한 클래스 이름이 "Yoga, Zumba, Pilates"로 결합되어 표시됩니다. 각 클래스 이름은 알파벳 순서로 정렬되어 쉼표로 구분되었습니다.

결론

GROUP_CONCAT 함수는 데이터를 집계하고 결합하는 데 매우 유용한 도구입니다. 이 함수는 특히 여러 값을 하나의 열에 결합하여 표시해야 할 때 유용하며, 데이터베이스 내에서 간단한 텍스트 조작을 할 수 있게 해줍니다. GROUP_CONCAT을 잘 활용하면 데이터베이스 쿼리를 더욱 강력하게 만들 수 있습니다.

이제 여러분도 GROUP_CONCAT을 사용하여 데이터를 그룹화하고, 필요에 따라 원하는 방식으로 결합해 보세요!

'Database' 카테고리의 다른 글

기존 테이블에 속성 추가  (0) 2021.11.11
동적 피벗 테이블  (0) 2021.01.27
mssql - ISNULL, IN, Procedure  (0) 2021.01.18
mssql - join, between, like '%'  (0) 2021.01.18

 

에러 상세 내역

e: file:///C:/Users/TUF/workspace/together-english-backend/src/main/kotlin/com/together_english/deiz/data/member/entity/Member.kt:23:5 Platform declaration clash: The following declarations have the same JVM signature (getPassword()Ljava/lang/String;):
    fun `<get-password>`(): String defined in cohttp://m.together_english.deiz.data.member.entity.Member
    fun getPassword(): String defined in cohttp://m.together_english.deiz.data.member.entity.Member

 

오랫만에 프로젝트를 실행해보니 위와같은 에러를 만나게 되었다. 확인해보니 UserDetails의 getPassword 메소드를 오버라이딩해야되기 때문에 다음과같이 선언했던 password를 return 하도록 만들었다.

    @JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
    var password: String = password
        private set
        
    @JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
    override fun getPassword(): String {
        return this.password
    }

코틀린에서는 변수의 getter와 setter를 자동으로 만들어주는 기능이 있기 때문에 password라는 변수가있으면 자동으로 getPassword 메소드를 만들게 된다. 하지만 이와 같은 이름의 메소드를 구현하였기 때문에 충돌하여 발생한 에러였다. 

 

해결책

UserDetails 인터페이스를 상속받아 getPassword 메소드를 오버라이딩 해야되기 때문에 해당 메소드의 명은 변경할 수 없다. 그렇기 때문에 password의 변수 명을 변경하여 해당 문제를 해결할 수 있다.

위 url은 ec2에 연결된 역할을 반환하는 url입니다. ec2에 연결된 역할이 없을 경우 해당 오류를 반환하게 됩니다.

해당 오류로 인해 프로그램이 실행되거나 프로세스를 진행할 때 문제가 되진 않습니다.

 

https://stackoverflow.com/questions/58378329/aws-instance-metadata-for-iam-is-not-found

 

AWS: instance metadata for iam is not found

I'm trying to set up elasticsearch s3 snapshots on my ec2 instances. And it fails with following error: nested: NotSerializableExceptionWrapper[sdk_client_exception: The requested metadata is not f...

stackoverflow.com

 

원인

자바 entity와 DB의 table이 일치하지 않아서 발생

생각했던 영어 질문지의 형태가 영어 질문, 한국어 해석으로 구성된 다는 것을 만든 후에 알게 되어 추후에 수정후 ddl을 update로 두고

실행하였더니 위와같은 에러가 발생하였다. ddl이 update로 설정돼있기 때문에 알아서 업데이트될 거라 생각했지만 DB를 확인해보니 업데이트되지 않았다. 

 

해결 방법

DB가 자동으로 변경되지 않은 점으로 보아 DB를 수정한대로 변경하면 해결될 것으로 추측했고 해결되었다.

 

아래 코드처럼 파라미터로 넘어오는 2개의 클래스가 있고 해당 클래스에는 id 라는 변수가 둘다 존재합니다. 이럴 때

source = 해당클래스.id 이런식으로도 매핑이 가능합니다.

@Mapper(componentModel = "spring")
interface QuestionMapper {

    fun toEntity(request: RequestSaveQuestion, member: Member): Question

    @Mapping(source = "question.id", target = "question_id")
    fun toResponse(question: Question, member: Member): QuestionResponse
}

 

이번에 여러 문제를 담을 table을 개발하고 쿼리스트링으로 여러 조건을 건내받기를 원했다. 이번에 선택한 방법은 하나의 파라미터에

difficulty=HARD-NORMAL 이런식으로 담겨오면 -값을 기준으로 스플릿하여 배열처럼 사용하였지만 id =1, id=2, id=3 이런식으로 하여서 배열처럼 받아올 수도 있는 것 같다 해당 방식은 아래 블로그를 참조하였다.

 

https://webisfree.com/2017-03-28/쿼리스트링-파라미터를-배열로-전달하는-방법은-어떤게-있을까요

 

쿼리스트링 파라미터를 배열로 전달하는 방법은 어떤게 있을까요?

자바스크립트에서 서버에 쿼리스트링(querystring)으로 파라미터 값을 전달하려고 합니다. 이때 전달 할 값이 하나가 아닌 여러 개인 경우 어떻게 하면 배열로 전달하는지 알아봅니다.

webisfree.com

 

Database Navigator -> DB선택 후 우클릭 -> Connection view -> Advanced 클릭

JPA 특징

  • JPA는 특정 데이터베이스에 종속되지 않는다.
  • 객체지향적인 프로그래밍을 할 수 있도록 도와준다.

Entity Manager Factory

  • 이름 그대로 Entity Manager를 생성하는 객체
  • 싱글턴 방식으로 사용
  • Entity Manager는 쓰레드간에 공유하면 안된다.
  • JPA의 모든 데이터 변경은 트랜잭션 안에서 실행해야한다.

영속성 컨텍스트(Persistence Context)

  • 엔티티를 영구 저장하는 환경
  • EntityManager.persist(entity)
  • 논리적인 개념
  • 엔티티 매니저를 통해서 영속성 컨텍스트에 접근할 수 있다.
  • 스프링 프레임워크 같은 컨테이너 환경에서는 엔티티 매니저와 영속성 컨텍스트가 N:1 관계를 가진다.

영속성 컨텍스트의 이점

  • 1차 캐시
    • persist가 진행되면 바로 DB로 저장되지 않고 영속성 컨텍스트 내부의 1차 캐시에 저장된다. 그리고 해당되는 sql은 영속성 컨텍스트 내부의 쓰기 지연 SQL에 저장된다.
    • find를 사용해서 조회하면 또한 1차캐시에 저장된다.
    • find 사용시 이미 1차 캐시에 저장되있으면 해당 캐시가 사용된다.
  • 동일성(identity) 보장
  • 트랜잭션을 지원하는 쓰기 지연(transactional write-behind)
  • 변경 감지(Dirty Checking)
  • 지연 로딩(Lazy Loading)

Entity Manager를 사용한 업데이트 방법

  • 업데이트 하기전 find를 통해서 해당 entity를 조회하게 된다. 그러면 영속성 상태가 되며 1차 캐시에 해당 엔티티가 올라가게 된다. 이 때 트랜잭션이 걸려있고 값을 변경하게 되면 트랜잭션이 끝나는 시점에 1차 캐시의 처음값이 담긴 스냅샷과 현재의 값을 비교하여 변경되면 update SQL을 쓰기 지연 SQL 저장소에 저장하게 되고 flush할때 DB로 update 쿼리가 실행되게 된다.

flush란?

  • 영속성 컨텍스트의 변경내용을 데이터베이스에 반영하는 작업이다.
  • 플러시가 발생되면 아래 동작이 실행된다.
    • 변경 감지
    • 수정된 엔티티 쓰기 지연 SQL 저장소에 등록
    • 쓰기 지연 SQL 저장소의 쿼리를 데이터베이스에 전송
    • (등록, 수정, 삭제 쿼리)

영속성 컨텍스트가 flush 되는 시점

  • entityManager.flush()가 실행될 때
  • 트랜잭션이 커밋 될 때
  • JPQL 쿼리 실행될 때 (모드를 설정하여 트랜잭션 커밋 시점에 실행되도록 변경할 수 있다.)

엔티티의 생명주기

  • 비영속 (new/transient)
    • 영속성 컨텍스트와 전혀 관계가 없는 새로운 상태
    • 단순히 객체를 생성한 상태
  • 영속 (managed)
    • 영속성 컨텍스트에 관리되는 상태
    • Entity Manager를 통해 persist를 수행한 상태
  • 준영속(detached)
    • 영속성 컨텍스트에 저장되었다가 분리된 상태
    • Entity manager의 detach, clear, close이 실행되면 영속 → 준영속이 된다.
  • 삭제 (removed) - 삭제된 상태

JPA 어노테이션

  • @Entity: JPA가 관리할 객체
  • @Id: 데이터베이스 PK와 매핑
  • @Table: 은 엔티티와 매핑할 테이블 지정 ex) @Table(name = “hello”)
  • @Column 컬럼 매핑
  • @Temporal 날짜 타입 매핑
    • 요즘은 LocalDate, LocalDateTime을 사용하기에 사용 x
  • @Enumerated enum 타입 매핑
    • 결과적으로 value = EnumType.STRING으로 사용해야 된다.
  • @Lob BLOB, CLOB 매핑
    • @Lob에는 지정할 수 있는 속성이 없다.
    • 매핑하는 필드 타입이 문자면 CLOB 매핑, 나머지는 BLOB 매핑
    • CLOB: String, char[], java.sql.CLOB
    • BLOB: byte[], java.sql.BLOB
  • @Transient 특정 필드를 컬럼에 매핑하지 않음(자바에서만 사용되는 변수)
    • 필드 매핑 x
    • 데이터베이스에 저장 x, 조회 x
    • 주로 메모리상에서만 임시로 어떤 값을 보관하고 싶을 때 사용

데이터베이스 스키마 자동 생성

  • DDL을 애플리케이션 실행 시점에 자동으로 생성한다.
  • 테이블 중심 → 객체 중심으로 변경
  • 데이터베이스 방언을 활용해서 데이터베이스에 맞는 적절한 DDL을 생성한다.
  • 이렇게 생성된 DDL은 개발 장비에서만 사용한다.
  • 생성된 DDL은 운영서버에서는 사용하지 않거나, 적절히 다듬은 후 사용된다.

hibernate.hbm2ddl.auto

  • DDL 생성 기능은 DDL을 자동 생성할 때만 사용되고 JPA의 실행로직에는 영향을 주지 않는다.

@SequenceGenerator

  • 아이디 값 위나 클래스 위에 사용
  • 주의: allocationSize 기본값은 50이다.
  • name : 식별자 생성기 이름
  • sequenceName 데이터베이스에 등록되어 있는 시퀸스 이름
  • initialValue DDL 생성 시에만 사용됨, 시퀸스 DDL을 생성할 때 처음 1 시작하는 수를 지정한다.
  • allocationSize 시퀸스 한 번 호출에 증가하는 수 (성능 최적화에 사용된다. 데이터베이스 시퀸스 값이 하나씩 증가하도록 설정되길 원하면 이 값을 반드시 1로 설정해야한다.)
    • 만약 위의 값을 1로 설정하면 데이터를 생성하고 db에 저장하고 조회하는 것을 하나의 데이터마다 수행하여 성능은 떨어질 수 있다.
  • catalog, schema: 데이터베이스 catalog, schema 이름

Incorrect string value: '\xEC\x98\x81\xEC\x96\xB4...'

 

위와 같은 에러가 나며 maria db를 사용한다면

 

ALTER TABLE 테이블 이름 convert to charset utf8;

위 명령어로 해결 가능하다. 위 명령어는 테이블의 charset 을 utf8로 변경하는 것이다.

ec2 프리티어에서 만든 프로젝트를 빌드해보았다. 하지만 예상외로 시간이 많이 걸려 빌드를 실패하였다.

한 번에 50분이 넘어가는 경우도 있었다. 이를 해결하기 위해서 빌드를 빠르게 하는 방법과 해당 문제를 가진 사람들이 있는지 서치한 결과 gradle.properties 파일에 해당 값을 넣어주는 것으로 17분 만에 빌드에 성공하였다.

 

# for memory issue
org.gradle.daemon=false

# memory allotted to JVM
org.gradle.jvmargs=-Xmx1024m 

# configure on demand
org.gradle.configureondemand=true

# parallel builds
org.gradle.parallel=true

# build Cache
android.enableBuildCache=true

# gradle caching
org.gradle.caching=true

램 1기가를 최대한 빌드하는데 쏘다 부으라는 ? 명령어로 보인다. 만약 gradle.properties 파일이 없다면 build.gradle 파일이 위치한 경로로 gradle.properties 파일을 생성해서 사용하면 된다. 

+ Recent posts