AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • Início
  • system&network
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • Início
  • system&network
    • Recentes
    • Highest score
    • tags
  • Ubuntu
    • Recentes
    • Highest score
    • tags
  • Unix
    • Recentes
    • tags
  • DBA
    • Recentes
    • tags
  • Computer
    • Recentes
    • tags
  • Coding
    • Recentes
    • tags
Início / dba / Perguntas / 121843
Accepted
carlo.borreo
carlo.borreo
Asked: 2015-11-24 03:44:28 +0800 CST2015-11-24 03:44:28 +0800 CST 2015-11-24 03:44:28 +0800 CST

Datas efetivas, requisitos complexos

  • 772

Tenho uma mesa com, digamos, cores preferidas. Para cada pessoa, armazenamos a cor favorita e a data efetiva. Se a pessoa mudar de preferência, armazenamos a nova com uma data efetiva mais recente. Exemplo:

name   color   eff_date
John   Green   2014-03-07
Luis   Red     2014-03-07
Luis   Yellow  2015-05-02
Nina   Blue    2015-07-06

Se quisermos pegar a cor preferida atual de Luis:

SELECT color
  FROM fav_colors
 WHERE name='Luis'
   AND eff_date=( SELECT max(eff_date) FROM fav_colors WHERE name='Luis' )

Agora decidimos que as pessoas podem mudar sua cor favorita a qualquer momento, mas a mudança só será efetivada a partir de 1º de janeiro. Portanto, mesmo que Luis tenha inserido um registro que diga que sua cor favorita é Amarelo, a consulta deve retornar o valor antigo (Vermelho) até 1º de janeiro de 2016.

SELECT color
  FROM fav_colors
 WHERE name='Luis'
   AND eff_date=( SELECT max(eff_date) FROM fav_colors WHERE name='Luis' and eff_date < '1 jan 2015' )

Esta consulta parece ok, mas não retornará nenhum registro para Nina. As regras dizem que um recém-chegado pode escolher a cor favorita imediatamente, sem esperar pelo próximo dia 1º de janeiro. Em outras palavras, "se possível, faça um registro de 1º de janeiro ou antes; se não for possível, qualquer registro; leve o mais recente". Isto é o que eu consegui escrever:

SELECT color
  FROM fav_colors
 WHERE name='Luis'
   AND eff_date= ISNULL(
       ( SELECT max(eff_date) FROM fav_colors WHERE name='Luis' and eff_date < '1 jan 2015' ),
       ( SELECT max(eff_date) FROM fav_colors WHERE name='Luis' )
       )

Existe uma maneira mais elegante ou mais eficiente de obter esse resultado?

Não tenho controle sobre a estrutura das tabelas, elas são da PeopleSoft.

Se Nina fez duas seleções este ano, quero mostrar a mais recente.

sql-server sql-server-2014
  • 4 4 respostas
  • 3234 Views

4 respostas

  • Voted
  1. Rob Farley
    2015-11-24T04:54:35+08:002015-11-24T04:54:35+08:00

    Eu explico como fazer isso aqui: http://blogs.lobsterpot.com.au/2014/07/08/ssis-lookup-transformation-in-t-sql/

    O conceito básico é usar OUTER APPLY com TOP (1). No seu caso, pode ser necessário começar com a lista de pessoas que você está procurando. Assim:

    SELECT p.Person, ISNULL(c1.color, c2.color) AS color
    FROM (VALUES ('Nina')) AS p(Person)
    OUTER APPLY 
        (SELECT TOP (1) f.color
        FROM fav_colors AS f
        WHERE f.name = p.Person
        AND f.eff_date <= @date
        ORDER BY f.eff_date DESC) c1
    OUTER APPLY
       (SELECT TOP (1) f.color
        FROM fav_colors AS f
        WHERE f.name = p.Person
        AND c1.color IS NULL
        ORDER BY f.eff_date ASC) c2
    

    A postagem detalha como o Query Optimizer só fará a segunda pesquisa se a primeira falhar. É realmente muito eficiente com o índice correto no lugar (em (name, eff_date) include (color)).

    Oh - se a cor puder ser NULL legitimamente, teste uma coluna diferente na subconsulta c2.

    • 4
  2. Best Answer
    ypercubeᵀᴹ
    2015-11-24T04:50:57+08:002015-11-24T04:50:57+08:00

    Você pode simplificar a primeira e a segunda consulta com TOP / ORDER BY:

    SELECT TOP (1) color
      FROM fav_colors
     WHERE name='Luis'
       AND eff_date < '2015-01-01'
    ORDER BY eff_date DESC ;
    

    No entanto, não retornará nada para Nina. Você pode usar outra subconsulta e combiná-las com UNION ALLmais uma TOPou com COALESCE()(ou ISNULL()como na sua consulta):

    SELECT color = COALESCE(
        ( SELECT TOP (1) color
            FROM fav_colors
           WHERE name='Luis'
             AND eff_date < '2015-01-01'
          ORDER BY eff_date DESC
        ),
        ( SELECT TOP (1) color
            FROM fav_colors
           WHERE name='Luis'
             AND eff_date >= '2015-01-01'
          ORDER BY eff_date ASC
        ) ) ;
    

    Se você quiser fazer esta verificação para mais de uma (ou todas as) pessoas na mesa, você pode usar OUTER APPLY, como @RobFarley's explica em sua resposta .)

    • 3
  3. James Anderson
    2015-11-24T05:04:40+08:002015-11-24T05:04:40+08:00

    Acho que você precisa alterar o tipo de dados da eff_datecoluna DATETIMEpara evitar problemas com pessoas fazendo duas ou mais seleções no mesmo dia.

    A PostYearStart_MAX_eff_datecoluna no CTEtalvez deva ser alterada MINdependendo da sua resposta à minha pergunta nos comentários.

    DECLARE @YearStart DATE = '2015-01-01', @Name NVARCHAR(50) = 'nina'
    
    DECLARE @fav_colors TABLE
    (
        name    NVARCHAR(50)
        ,color  NVARCHAR(25)
        ,eff_date   DATETIME -- DATETIME needed to differentitate between two or more selections made on the same day
    )
    
    INSERt INTO @fav_colors
    SELECT 'John', 'Green', '2014-03-07'
    UNION
    SELECT  'Luis', 'Red', '2014-03-07'
    UNION 
    SELECT  'Luis', 'Yellow', '2015-05-02'
    UNION
    SELECT  'Nina', 'Blue', '2015-07-06';
    
    
    WITH CTE_Colors
    AS
    (
        SELECT  name
                ,COUNT(1) AS SelectionCount
                ,MAX(eff_date) MAX_eff_date
                ,SUM(CASE WHEN eff_date < @YearStart THEN 1 ELSE 0 END) AS PreYearStartSelectionCount 
                ,MAX(CASE WHEN eff_date < @YearStart THEN eff_date ELSE '1900-01-01' END) AS PreYearStart_MAX_eff_date
                ,SUM(CASE WHEN eff_date >= @YearStart THEN 1 ELSE 0 END) AS PostYearStartSelectionCount 
                ,MAX(CASE WHEN eff_date >= @YearStart THEN eff_date ELSE '1900-01-01'  END) AS PostYearStart_MAX_eff_date
    
        FROM    @fav_colors
    
        WHERE   name = @Name
    
        GROUP BY name
    )
    
    SELECT      fc.color
    
    FROM        @fav_colors fc
    LEFT JOIN   CTE_Colors cte ON fc.name = cte.name
    
    WHERE       CASE 
                    WHEN SelectionCount = 1
                    THEN MAX_eff_date
                    WHEN PreYearStartSelectionCount > 0 
                    THEN PreYearStart_MAX_eff_date
                    WHEN PostYearStartSelectionCount > 0    
                    THEN PostYearStart_MAX_eff_date                 
                END = fc.eff_date
    
    • 0
  4. Stoleg
    2015-11-24T06:52:20+08:002015-11-24T06:52:20+08:00

    Seu problema vem do fato de você estar misturando/usando a mesma coluna para dois tipos de informação:

    • Escolha feita / data de entrada de dados
    • Data efetiva - quando aplicar a escolha de

    Para tornar seu modelo de dados mais elegante e dar mais liberdade, considere adicionar a coluna "Data atualizada". Em seguida, você pode preenchê-lo com as informações da coluna "Data efetiva" que está usando agora. Ao mesmo tempo, a coluna "Data Efetiva" presente conterá a Data Efetiva real, por exemplo, 1º de janeiro de 2015.

    • 0

relate perguntas

  • SQL Server - Como as páginas de dados são armazenadas ao usar um índice clusterizado

  • Preciso de índices separados para cada tipo de consulta ou um índice de várias colunas funcionará?

  • Quando devo usar uma restrição exclusiva em vez de um índice exclusivo?

  • Quais são as principais causas de deadlocks e podem ser evitadas?

  • Como determinar se um Índice é necessário ou necessário

Sidebar

Stats

  • Perguntas 205573
  • respostas 270741
  • best respostas 135370
  • utilizador 68524
  • Highest score
  • respostas
  • Marko Smith

    conectar ao servidor PostgreSQL: FATAL: nenhuma entrada pg_hba.conf para o host

    • 12 respostas
  • Marko Smith

    Como fazer a saída do sqlplus aparecer em uma linha?

    • 3 respostas
  • Marko Smith

    Selecione qual tem data máxima ou data mais recente

    • 3 respostas
  • Marko Smith

    Como faço para listar todos os esquemas no PostgreSQL?

    • 4 respostas
  • Marko Smith

    Listar todas as colunas de uma tabela especificada

    • 5 respostas
  • Marko Smith

    Como usar o sqlplus para se conectar a um banco de dados Oracle localizado em outro host sem modificar meu próprio tnsnames.ora

    • 4 respostas
  • Marko Smith

    Como você mysqldump tabela (s) específica (s)?

    • 4 respostas
  • Marko Smith

    Listar os privilégios do banco de dados usando o psql

    • 10 respostas
  • Marko Smith

    Como inserir valores em uma tabela de uma consulta de seleção no PostgreSQL?

    • 4 respostas
  • Marko Smith

    Como faço para listar todos os bancos de dados e tabelas usando o psql?

    • 7 respostas
  • Martin Hope
    Jin conectar ao servidor PostgreSQL: FATAL: nenhuma entrada pg_hba.conf para o host 2014-12-02 02:54:58 +0800 CST
  • Martin Hope
    Stéphane Como faço para listar todos os esquemas no PostgreSQL? 2013-04-16 11:19:16 +0800 CST
  • Martin Hope
    Mike Walsh Por que o log de transações continua crescendo ou fica sem espaço? 2012-12-05 18:11:22 +0800 CST
  • Martin Hope
    Stephane Rolland Listar todas as colunas de uma tabela especificada 2012-08-14 04:44:44 +0800 CST
  • Martin Hope
    haxney O MySQL pode realizar consultas razoavelmente em bilhões de linhas? 2012-07-03 11:36:13 +0800 CST
  • Martin Hope
    qazwsx Como posso monitorar o andamento de uma importação de um arquivo .sql grande? 2012-05-03 08:54:41 +0800 CST
  • Martin Hope
    markdorison Como você mysqldump tabela (s) específica (s)? 2011-12-17 12:39:37 +0800 CST
  • Martin Hope
    Jonas Como posso cronometrar consultas SQL usando psql? 2011-06-04 02:22:54 +0800 CST
  • Martin Hope
    Jonas Como inserir valores em uma tabela de uma consulta de seleção no PostgreSQL? 2011-05-28 00:33:05 +0800 CST
  • Martin Hope
    Jonas Como faço para listar todos os bancos de dados e tabelas usando o psql? 2011-02-18 00:45:49 +0800 CST

Hot tag

sql-server mysql postgresql sql-server-2014 sql-server-2016 oracle sql-server-2008 database-design query-performance sql-server-2017

Explore

  • Início
  • Perguntas
    • Recentes
    • Highest score
  • tag
  • help

Footer

AskOverflow.Dev

About Us

  • About Us
  • Contact Us

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve