UTC Time 값을 조회하는 쿼리

SELECT GETUTCDATE() AS UTC_TIME

 

UTC Time값을 저장한 Datatime 변수를 Local Time으로 조회하는 함수

 

DECLARE @DT1 DATETIME
      , @DT2 DATETIME

    SELECT @DT1 = GETUTCDATE()    
	
    SELECT @DT1    // 출력값 : 2025-02-26 08:24:22.130

    SELECT @DT2 = SWITCHOFFSET(CONVERT(DATETIMEOFFSET, @DT1),DATENAME(TZOFFSET, SYSDATETIMEOFFSET()))
	
    SELECT @DT2    // 출력값 : 2025-02-26 17:24:22.130

 

 

아래 스택 오버플로우 글을 참고함.

https://stackoverflow.com/questions/8038744/convert-datetime-column-from-utc-to-local-time-in-select-statement

문제 : NodeJS로 구성된 API서버에서 입력한 시간을 MSSQL 서버에 입력 시 간혹 1 밀리세컨드 차이가 발생함.

원인 : NodeJS에서 MSSQL로 쿼리 생성 후 전달 시 매개변수 타입을 의도와 다르게 DateTime 으로 설정함.

해결 : MSSQL 컬럼 타입과 매개변수 타입을 DateTime2로 변경.

 

상세 내용:

의도

데이터 입력 시간을 밀리세컨드 까지 기록하려 함. [예시) 2025-01-01 13:01:00.001]

API서버는 NodeJs로 구성되어 있고, mssql 라이브러리를 require 로 가져와서 사용함.

const msdb = require('mssql');

async dbtest() {
        let now = new Date();
        console.log(now); //2025-01-01 13:01:00.001
        return (await msdb.connect(conf.db)).request()
            .input('RMK', msdb.NVarChar, 'Test Remark')
            .input('REG_DT', msdb.DateTime, now)
            .query(`insert into tb_logs (RMK, REG_DT) values (@RMK, @REG_DT)`);
    }

 

문제 확인

MSSQL DB에서 Select 쿼리로 조회해보면, 간혹 한번씩 1밀리세컨드가 더 크게 들어가 있는 것을 확인함.

console.log로 찍은건 밀리세컨드가 001 인데 MSSQL 서버에서 002로 조회됨.

MSSQL 컬럼 타입을 DateTime2로 변경해서 테스트한 후 문제 원인 추측할 수 있었음.

console.log에서 [2025-01-01 13:01:00.001] 이렇게 보이던 값이

DB에 [2025-01-01 13:01:00.0016667] 과 같이 들어가 있었고, 조회될 때 4번째에서 반올림되어 002로 조회되었음.

 

문제 해결
MSSQL 컬럼 타입과 Node.js API서버의 매개변수 타입을 msdb.DateTime2로 변경해준 뒤 

DB에 [2025-01-01 13:01:00.0010000] 으로 저장됨 확인.

const msdb = require('mssql');

async dbtest() {
        let now = new Date();
        console.log(now); //2025-01-01 13:01:00.001
        return (await msdb.connect(conf.db)).request()
            .input('RMK', msdb.NVarChar, 'Test Remark')
            .input('REG_DT', msdb.DateTime2, now) //msdb.DateTime -> msdb.DateTime2
            .query(`insert into tb_logs (RMK, REG_DT) values (@RMK, @REG_DT)`);
    }

 

-- 위 코드는 상황을 설명하기 위해 재구성하여 작성하였기에 정상적으로 동작하지 않을 수 있습니다.

 

-- 개인적인 생각. 

공식 문서에는 DateTime2가 0.0000001 초까지 기록된다고 되어있는데, node의 mssql 라이브러리 버그인걸까?

mssql은 7.3.5 버전을 사용중...

Nodejs로 API 서버 구현 중 MSSQL을 사용한다.

2개의 다른 MSSQL 서버와 연결하여 데이터를 활용하려 한다.

* 아래 코드는 테스트를 위해 임시로 만든 코드이다.

 

문제 : 아래와 같이 2개 MSSQL 서버 속성을 다르게 설정하고, mssql 모듈을 2번 불러와 사용하려 했는데,

          최초 연결한 DB 서버가 pool에 들어가 있어서 인지, 다른 속성으로 Connect를 시도하여도

          이미 pool이 생성되어있던 DB서버에 쿼리를 날려버린다.

// db.js
let conf = require(./conf);

const msdb1 = require('mssql');
const msdb2 = require('mssql');

module.exports = {

    async dbtest1() {
        return (await msdb1.connect(conf.db1)).request()
            .input('NM_ID', msdb1.NVarChar, 'test_id1')
            .input('RMK', msdb1.NVarChar, 'TEST Remark1')
            .query(`insert into tb_logs (NM_ID, RMK) values (@NM_ID, @RMK)`);
    }
    
    
    async dbtest2() {
        return (await msdb2.connect(conf.db2)).request()
            .input('NM_ID', msdb2.NVarChar, 'test_id2')
            .input('RMK', msdb2.NVarChar, 'TEST Remark2')
            .query(`insert into tb_logs (NM_ID, RMK) values (@NM_ID, @RMK)`);
    }
}

 

 

해결 방안 : 스택 오버플로우에 답변을 올려주신 분이 있어서, 해당 코드를 참고하였다. (매우 감사한 분이다.)

                  GetCreateIfNotExistPool 함수명이 싫어서 기존과 동일한 connect 로 함수를 변경하여 사용했다.

                  1. 여러 MSSQL DB의 커넥션 풀을 관리하는 mssql-connection-pooling.js 파일을 하나 만든다.

                      - 이 파일에서는 여러 MSSQL DB의 커넥션 풀을 pools 객체에 넣어관리한다.
                      config 객체를 문자열로 만든 뒤 key로 사용한다.

                      connection을 요청할 때 동일한 key가 있다면 pools에서 해당 pool을 제공한다.

                      동일한 key가 없으면 새로운 pool을 연결하여 pools 에 새로 넣어놓고 pool을 제공한다.

                  2. mssql-connection-pooling.js 파일을 기존 코드에 불러와 사용한다.

 

// mssql-connection-pooling.js 파일 코드
const { ConnectionPool } = require('mssql');
const pools = {};

// 새로운 커넥션 풀 생성
function CreatePool(config) {
    let key = JSON.stringify(config);

    if (GetPool(key)) {
        throw new Error('Pool already exists');
    }

    pools[key] = (new ConnectionPool(config)).connect();
    return pools[key];
}

// pools 안에 생성된 connection pool 받기
function GetPool(name) {
    if (pools[name]) {
        return pools[name];
    } else {
        return null;
    }
}

// pool이 생서되어 있으면 return pool, 없으면 새로 생성
function connect(config) {
    let key = JSON.stringify(config);

    let pool = GetPool(key);
    if (pool) {
        return pool;
    } else {
        return CreatePool(config);
    }
}

function ClosePool(config) {
    let key = JSON.stringify(config);

    if (pools[key]) {
        const pool = pools[key];
        delete pools[key];
        pool.close();
        return true;
    }
    return false;
}

// 모든 pool 종료
function CloseAllPools() {
    pools.forEach((pool) => {
        pool.close();
    });
    pools = {};
    return true;
}

module.exports = {
    ClosePool,
    CloseAllPools,
    CreatePool,
    GetPool,
    connect,
}

 

// db.js //수정본
let conf = require(./conf);

const msdb = require('mssql');
const mssql = require('./mssql-connection-pooling.js')
//const msdb1 = require('mssql');
//const msdb2 = require('mssql');


module.exports = {
    async dbtest1() {
        return (await mssql.connect(conf.db1)).request()
            .input('NM_ID', mssql.NVarChar, 'test_id1')
            .input('RMK', mssql.NVarChar, 'TEST Remark1')
            .query(`insert into tb_logs (NM_ID, RMK) values (@NM_ID, @RMK)`);
    }
    
    
    async dbtest2() {
        return (await mssql.connect(conf.db2)).request()
            .input('NM_ID', mssql.NVarChar, 'test_id2')
            .input('RMK', mssql.NVarChar, 'TEST Remark2')
            .query(`insert into tb_logs (NM_ID, RMK) values (@NM_ID, @RMK)`);
    }   
}

 

참조 : https://stackoverflow.com/questions/64254145/node-js-mssql-multiple-concurrent-connections-to-sql-servers-interfering-with-ea

 

Node.js mssql multiple concurrent connections to SQL servers interfering with each other

I am using mssql in my Node.js express application to make connections to many different databases across many different SQL servers. I have constructed the following example to demonstrate the gen...

stackoverflow.com

 

 

Vue2에서 eslint-plugin-vue를 사용한다면, 하위 컴포넌트의 slot에 v-text 또는 v-html을 전달하여 사용할 때 간혹 아래와 같은 오류가 발생한다.

 

vue/no-v-text-v-html-on-component error

 

원인은 아래 코드와 같이 하위 컴포넌트를 사용하며 v-text를 속성으로 전달하여 사용하도록 작성된 것이다.

<child-component v-text="ABC" />

 

v-text를 속성으로 전달할 경우, 하위 컴포넌트의 콘텐츠를 덮어쓰기 하며 컴포넌트가 정상적으로 동작하지 않을 수 있기 때문에 eslint-plugin-vue에서 error 로 표시한다.

 

해결 방법은 v-text를 하위 컴포넌트 내부에 div 태그나 p 태그로 전달하면 된다.

<child-component>
	<div v-text="ABC"/>
</child-component>

 

 

다른 방법으로는 eslint에서 특정 라인을 검사하지 않도록 처리하는 방법이 있으나, 추천하진 않는다.

<!-- eslint-disable-next-line vue/no-v-text-v-html-on-component -->
<child-component v-text="ABC" />

 

해당 오류를 수정하기 위해서 아래 사이트를 참고하였다.

https://eslint.vuejs.org/rules/no-v-text-v-html-on-component.html

 

vue/no-v-text-v-html-on-component | eslint-plugin-vue

 

eslint.vuejs.org

https://stackoverflow.com/questions/75698069/disable-eslint-error-with-vuejs-and-v-html

 

Disable eslint error with Vuejs and v-html

I use Nuxt 2 with Vue 2 and vuetify. My vscode was updated and I am getting an eslint error with v-html. The code is: <v-list-item-title v-html="`${parent.genFilteredText(item.nome)}`&q...

stackoverflow.com

 

SSMS에서 임시테이블을 사용하여 데이터를 확인하는 쿼리를 사용하다보면,

임시테이블을 제거(DROP)하고 다시 생성해서 사용해야하는 경우가 있다.

 

쿼리의 마지막 라인에 사용한 임시테이블을 제거(DROP)하는 방법으로 쿼리 작성도 가능하지만

실행 시 임시테이블이 있는지 확인하고 있다면 지우고 다시 생성하도록 하는 방법도 있다.

 

    SELECT *
      INTO #TEMP_TT
      FROM (
            SELECT 'A' AS T UNION ALL
            SELECT 'B' AS T UNION ALL
            SELECT 'C' AS T
           ) A

    SELECT *
      FROM #TEMP_TT

<임시테이블에 데이터를 입력하고 입력된 데이터를 확인하는 쿼리>

위 쿼리를 처음 실행하면 아래와 같이 정상적으로 실행된다.

정상실행 화면

 

위 쿼리를 다시한번 실행하면 아래와 같은 오류가 발생한다.

MSSQL ERROR MESSAGE - 메시지 2714, 수준 16, 상태 6, 줄 2 데이터베이스에 '#TEMP_TT'(이)라는 개체가 이미 있습니다.

 

쿼리 상단에 아래와 같은 코드를 넣어주면, 쿼리 실행 시 임시테이블의 존재 여부를 확인하고 

임시테이블이 있다면 제거 후 쿼리를 실행하게 된다.

* #이 2개 붙어있는 전역 임시테이블에 사용할 때는 다른 SESSION에서 사용되고 있을 수 있으니 주의해야한다.

    IF OBJECT_ID('TEMPDB..#TEMP_TT') IS NOT NULL
    BEGIN
        DROP TABLE #TEMP_TT
        PRINT 'DROP TABLE #TEMP_TT.'
    END
    ELSE
    BEGIN
        PRINT '#TEMP_TT IS NOT EXISTS.'
    END

    SELECT *
      INTO #TEMP_TT
      FROM (
            SELECT 'A' AS T UNION ALL
            SELECT 'B' AS T UNION ALL
            SELECT 'C' AS T
           ) A

    SELECT *
      FROM #TEMP_TT

임시테이블이 있다면 제거 후 쿼리를 실행할 수 있도록 조건문 추가.

 

위 쿼리를 실행하면 아래와 같이 임시테이블 존재 여부에 따라 메시지가 표시되며, 정상 실행된다.

임시테이블 확인 후 제거
정상실행

 

취미용으로 장난감을 만들며 놀기 위해 1.29 inch OLED LCD 화면을 구입했습니다.

 

제품 설명에 CH1115 호환 SSD1306 이라고 적혀있기에 SSD1306을 검색하여

 

가장 많이 나오는 Adafruit_SSD1306 라이브러리를 사용하여 테스트 용 코드를 작성하였습니다.

 

결과는 아래 사진과 같이 나와서 깜짝 놀랐습니다.

 

Adafruit_SSD1306.h 사용

 

어디가 잘못된건지 감도 못잡겠고, SSD1306, CH1115 로 검색해봐도 Adafruit 라이브러리만 보여서 답답했습니다.

 

결과적으로 유튜브 영상에서 U8glib 라이브러리를 알게 되었습니다.

 

아래는 U8glib 라이브러리를 사용하여 테스트용 코드를 작성했을 때 모습입니다.

 

U8glib.h 사용

 

테스트에 사용한 코드를 같이 남겨놓습니다.

#include "U8glib.h"

U8GLIB_SSD1306_128X64 u8g(U8G_I2C_OPT_DEV_0 | U8G_I2C_OPT_NO_ACK | U8G_I2C_OPT_FAST); // Fast I2C / TWI

int progress = 0;

void setup() {
  // put your setup code here, to run once:
  u8g.setFont(u8g_font_tpssb);
  u8g.setColorIndex(1);
}

void loop() {
  // put your main code here, to run repeatedly:
  u8g.firstPage();
  do{
    u8g.drawStr(25,50,"Progress Bar");
    u8g.drawFrame(0, 10, 128, 20);
    u8g.drawBox(10,15,progress, 10);
  } while (u8g.nextPage());

  if (progress < 108)
  {
    progress++;
  } 
  else 
  {
    progress = 0;
  }
}

 

아래에 구매한 제품과 참고한 유튜브 영상의 링크를 같이 남겨놓습니다.

 

 

 

숫자형 데이터를 문자형 데이터로 변경할 때 앞에 0을 채워서 지정된 길이로 만들어 사용하고 싶을 때가 있습니다.

단순히 CONVERT를 사용하면 앞에 0이 채워지지 않은 상태로 숫자만 문자형으로 변경되기 때문에

다른 방법을 사용해야 합니다.

 

FORMAT 함수를 사용하면 앞에 0을 채워 지정한 길이의 문자열로 만들어 사용할 수 있습니다.

 

    -- FORMAT 함수를 사용하여 숫자를 10자리 문자열로 변경
    SELECT FORMAT(1230,'0000000000') AS FORMATTED_TEXT

    -- CONVERT 함수를 사용하여 숫자를 문자로 변경
    SELECT CONVERT(NVARCHAR(10),1230) AS CONVERTED_TEXT

 

위 코드를 실행하면 아래와 같은 결과가 나옵니다.

코드 실행 결과

 

기타 다른 상황에 대한 실행 결과

    -- 숫자 길이가 서식보다 길 때
    SELECT FORMAT(12345678901234567890,'0000000000') AS FORMATTED_OVER_TEXT
    
    -- 숫자를 3자리 마다 쉼표로 구분하고 싶을 때
    SELECT FORMAT(12345678901234567890,'0,000,000,000') AS FORMATTED_COMMAS_TEXT

    -- 숫자 앞에 문자를 고정으로 넣어주고 싶을 때
    SELECT FORMAT(12345678901234567890,'FORMATTED_0000000000') AS FORMATTED_COMMAS_TEXT

코드 실행 결과

LAG 함수를 통해 이전 데이터의 값을 가져와 계산에 사용할 수 있다.

SQL Server2012버전 이후 부터 사용 가능하다.

 

사용방법

 

SELECT 문에 함수 작성.

LAG(컬럼명, 이전 행 수, 이전 행이 없을 때 기본값) OVER ( PARTITION BY 그룹으로 묶을 컬럼 리스트

                                                                                               ORDER BY 이전행을 찾을 때 사용할 정렬 기준)

파란색 으로 표시한 글자 생략 가능

/*********************************
* 이전 행 데이터 가져와 계산 TEST 쿼리
**********************************/

-- 1. TEST에 필요한 BASE DATA 만들기

    SELECT A.ITEM
         , A.VALUE
      INTO #TEMP1
      FROM (
            SELECT 'A' ITEM, 1 VALUE UNION ALL
            SELECT 'A' ITEM, 2 VALUE UNION ALL
            SELECT 'A' ITEM, 3 VALUE UNION ALL
            SELECT 'A' ITEM, 4 VALUE UNION ALL
            SELECT 'A' ITEM, 5 VALUE UNION ALL
            SELECT 'B' ITEM, 6 VALUE UNION ALL
            SELECT 'B' ITEM, 7 VALUE UNION ALL
            SELECT 'B' ITEM, 8 VALUE UNION ALL
            SELECT 'B' ITEM, 9 VALUE UNION ALL
            SELECT 'B' ITEM, 10 VALUE
           ) A

-- 2. BASE DATA 확인

    SELECT *
      FROM #TEMP1

-- 3. LAG 윈도우 함수 사용

    SELECT ITEM
         , VALUE
         , LAG(VALUE,1,0) OVER( PARTITION BY ITEM ORDER BY VALUE ) STACKED_SUM
      FROM #TEMP1

 

MSDN 참고

https://learn.microsoft.com/ko-kr/sql/t-sql/functions/lag-transact-sql?view=sql-server-ver16 

 

LAG(Transact-SQL) - SQL Server

LAG(Transact-SQL)

learn.microsoft.com

 

 

MSSQL Server가 재시작된 이후의 Index별 사용 횟수를 조회함.

VIEW SERVER STATE 권한이 있는 sa 와 같은 계정으로 실행해야 정상 실행됨.

 

-- Table의 Index 별 사용 횟수 확인.
-- USER_SEEK : 정상적으로 INDEX만 사용하여 검색됨.
-- USER_SCANS : INDEX 내에서 SCAN됨.
-- USER_LOOKUPS : INDEX 내에서 조회됨.
-- RANGE_SCAN_COUNT : INDEX에서 범위로 찾은 후 데이터에서 다시 조회됨.
--                    INDEX KEY, PK 외 다른 조건이 붙어 있는 경우.

DECLARE @V_WORK_DATE NVARCHAR(8) = CONVERT(NVARCHAR,GETDATE(),112)
    SELECT LEFT(@V_WORK_DATE,4) + '-' + SUBSTRING(@V_WORK_DATE,5,2) + '-' + RIGHT(@V_WORK_DATE,2) AS WORK_DATE
         , OBJECT_NAME(IUS.OBJECT_ID) AS TABLE_NAME
         , I.NAME AS INDEX_NAME
         , IUS.INDEX_ID
         , IZ.INDEX_SIZE
         , IUS.USER_SEEKS
         , IUS.USER_SCANS
         , IUS.USER_LOOKUPS
         , IOS.RANGE_SCAN_COUNT
      FROM SYS.DM_DB_INDEX_USAGE_STATS AS IUS
           INNER JOIN SYS.INDEXES AS I
        ON I.OBJECT_ID = IUS.OBJECT_ID
       AND I.INDEX_ID  = IUS.INDEX_ID
           INNER JOIN SYS.DM_DB_INDEX_OPERATIONAL_STATS(DB_ID(),null,NULL,NULL) AS IOS
        ON IOS.OBJECT_ID = IUS.OBJECT_ID
       AND IOS.INDEX_ID = IUS.INDEX_ID
           INNER JOIN
           (
            SELECT I.OBJECT_ID
                 , I.INDEX_ID AS INDEX_ID
                 , SUM(AU.USED_PAGES) * 8 / 1024 AS 'INDEX_SIZE'
              FROM SYS.INDEXES I
                   INNER JOIN SYS.PARTITIONS AS P
                ON P.OBJECT_ID = I.OBJECT_ID
               AND P.INDEX_ID   = I.INDEX_ID
                   INNER JOIN SYS.ALLOCATION_UNITS AS AU
                ON AU.CONTAINER_ID = P.PARTITION_ID
             WHERE 1=1--I.object_id = OBJECT_ID(@V_TABLE_NAME)
          GROUP BY I.OBJECT_ID
                 , I.INDEX_ID
           ) AS IZ
        ON IZ.OBJECT_ID = I.OBJECT_ID
       AND IZ.INDEX_ID  = I.INDEX_ID
     WHERE 1=1 --IUS.OBJECT_ID = OBJECT_ID(@V_TABLE_NAME)
       AND IUS.DATABASE_ID = DB_ID()
    ORDER BY 1,2,4

 

자세한 사항은 msdn 참고

https://learn.microsoft.com/ko-kr/sql/relational-databases/system-dynamic-management-views/sys-dm-db-index-usage-stats-transact-sql?view=sql-server-ver16

 

sys.dm_db_index_usage_stats(Transact-SQL) - SQL Server

sys.dm_db_index_usage_stats(Transact-SQL)

learn.microsoft.com

 

1. Visual Studio 편집기  테마 모음 사이트에서 Visual Studio 버전에 맞는 테마 다운로드.

2. Visual Studio 도구에서 설정 가져오기로 테마 적용.

 

편집기 테마 모음 사이트 

https://studiostyl.es/

 

Studio Styles - Visual Studio color schemes

public class Snippet : IThemeable { static void Main() { if("hello".Length < (43 ^ 2)) new Uri("http://there.com"); } }

studiostyl.es

 

1. Visual Studio 편집기  테마 모음 사이트에서 Visual Studio 버전에 맞는 테마 다운로드.

 

1-1. 미리보기를 보고 취향에 맞는 테마 이름을 선택.

1-2. Visual Studio 버전을 선택하고 'Download this scheme' 버튼 선택하여 다운로드.

 

2. Visual Studio 도구에서 설정 가져오기로 테마 적용.

 

2-1. 도구>설정 가져오기 및 내보내기(I)... 선택

 

2-2. '선택한 환경 설정 가져오기'를 선택하고 '다음'

2-3. '예,현재 설정을 저장합니다.', 선택 후 '다음'

       현재 설정값 저장할 이름과 경로 설정

       (새로운 테마 적용 후 문제 발생 시 기존으로 복원할 설정값 백업, 사진과 같이 설정하면 바탕화면에 생김.)

2-4. '찾아보기'로 다운받은 설정값 불러와 선택 후 다음.

2-5. '마침' 선택 하여 가져오기 종료.

2-5까지 마치면, 편집기 테마가 변경됨.

'Visual Studio' 카테고리의 다른 글

[vs]소스코드 폴딩 단축키(접기 펴기)  (0) 2020.10.26

+ Recent posts