Requête SQL de comparer les ventes de produits par mois
-
08-06-2019 - |
Question
J'ai un Statut Mensuel vue de base de données j'ai besoin de créer un rapport basé sur.Les données de la vue ressemble à quelque chose comme ceci:
Category | Revenue | Yearh | Month
Bikes 10 000 2008 1
Bikes 12 000 2008 2
Bikes 12 000 2008 3
Bikes 15 000 2008 1
Bikes 11 000 2007 2
Bikes 11 500 2007 3
Bikes 15 400 2007 4
...Et ainsi de suite
Le point de vue a une catégorie de produits, des recettes, un an et un mois.Je veux créer un rapport de comparaison de 2007 et de 2008, montrant la valeur 0 pour le mois, avec aucune vente.Ainsi, le rapport devrait ressembler à quelque chose comme ceci:
Category | Month | Rev. This Year | Rev. Last Year
Bikes 1 10 000 0
Bikes 2 12 000 11 000
Bikes 3 12 000 11 500
Bikes 4 0 15 400
La clé de chose à remarquer, c'est le mois 1 seule un chiffre d'affaires en 2008, et est donc 0 pour 2007.Aussi, 4 mois seulement n'a pas de chiffre d'affaires en 2008, d'où le 0, alors qu'il a chiffre d'affaires en 2007 et encore.
Aussi, le rapport est en réalité financière de l'année donc j'aimerais avoir des colonnes vides avec des 0 dans les deux cas il n'y a pas de ventes à dire 5 mois, soit en 2007 ou en 2008.
La requête que j'ai obtenu ressemble à quelque chose comme ceci:
SELECT
SP1.Program,
SP1.Year,
SP1.Month,
SP1.TotalRevenue,
IsNull(SP2.TotalRevenue, 0) AS LastYearTotalRevenue
FROM PVMonthlyStatusReport AS SP1
LEFT OUTER JOIN PVMonthlyStatusReport AS SP2 ON
SP1.Program = SP2.Program AND
SP2.Year = SP1.Year - 1 AND
SP1.Month = SP2.Month
WHERE
SP1.Program = 'Bikes' AND
SP1.Category = @Category AND
(SP1.Year >= @FinancialYear AND SP1.Year <= @FinancialYear + 1) AND
((SP1.Year = @FinancialYear AND SP1.Month > 6) OR
(SP1.Year = @FinancialYear + 1 AND SP1.Month <= 6))
ORDER BY SP1.Year, SP1.Month
Le problème avec cette requête, c'est qu'il ne serait pas de retour de la quatrième rangée dans mon exemple, les données ci-dessus, puisque nous n'avons pas de chiffre d'affaires en 2008, mais nous avons fait en 2007.
C'est probablement une commune de la requête/problème, mais mon SQL est rouillé, après avoir fait front-end développement depuis si longtemps.Toute aide est grandement appréciée!
Oh, btw, je suis à l'aide de SQL 2005 pour cette question, donc si il y a tout utile de nouvelles fonctionnalités qui pourraient m'aider à me le faire savoir.
La solution
L'Instruction case est mon meilleur sql ami.Vous avez également besoin d'une table pour générer votre 0 rev dans les deux mois.
Les hypothèses sont fondées sur la disponibilité des tables suivantes:
ventes:Category | Recettes | Yearh | Mois
et
tm:Année | Mois (rempli avec tous les les dates de déclaration nécessaires)
Exemple 1 sans les lignes vides:
select
Category
,month
,SUM(CASE WHEN YEAR = 2008 THEN Revenue ELSE 0 END) this_year
,SUM(CASE WHEN YEAR = 2007 THEN Revenue ELSE 0 END) last_year
from
sales
where
year in (2008,2007)
group by
Category
,month
RETOURNE:
Category | Month | Rev. This Year | Rev. Last Year
Bikes 1 10 000 0
Bikes 2 12 000 11 000
Bikes 3 12 000 11 500
Bikes 4 0 15 400
Exemple 2 avec des lignes vides:Je vais utiliser une sous-requête (mais d'autres ne peuvent pas) et sera de retour une ligne vide pour chaque produit et de l'année, le mois de combo.
select
fill.Category
,fill.month
,SUM(CASE WHEN YEAR = 2008 THEN Revenue ELSE 0 END) this_year
,SUM(CASE WHEN YEAR = 2007 THEN Revenue ELSE 0 END) last_year
from
sales
Right join (select distinct --try out left, right and cross joins to test results.
product
,year
,month
from
sales --this ideally would be from a products table
cross join tm
where
year in (2008,2007)) fill
where
fill.year in (2008,2007)
group by
fill.Category
,fill.month
RETOURNE:
Category | Month | Rev. This Year | Rev. Last Year
Bikes 1 10 000 0
Bikes 2 12 000 11 000
Bikes 3 12 000 11 500
Bikes 4 0 15 400
Bikes 5 0 0
Bikes 6 0 0
Bikes 7 0 0
Bikes 8 0 0
Notez que la plupart des outils de reporting fera ce tableau croisé ou de la matrice de la fonctionnalité, et maintenant que j'y pense SQL Server 2005 a pivot de la syntaxe qui va le faire ainsi.
Voici quelques ressources supplémentaires.CAS http://www.4guysfromrolla.com/webtech/102704-1.shtml SQL SERVER 2005 PIVOT http://msdn.microsoft.com/en-us/library/ms177410.aspx
Autres conseils
@Christian -- démarque de l'éditeur -- POUAH;en particulier lors de la prévisualisation et la version finale de votre post en désaccord...@Christian -- jointure externe complète -- la jointure externe complète est contredit par le fait qu'il y a des références à SP1 dans la clause where la clause where est appliqué après le REJOINDRE.Pour faire une jointure externe complète avec le filtrage sur l'une des tables, vous devez mettre votre clause where dans une sous-requête, de sorte que le filtrage se passe avant la jointure, ou essayer de construire l'ensemble de vos critères sur la JOINTURE SUR la clause, qui est incroyablement laid.Eh bien, il n'y a effectivement pas de jolie façon de faire de celui-ci.
@Jonas:Considérant ceci:
Aussi, le rapport est en fait pour l'année financière afin de J'aimerais avoir des colonnes vides avec des 0 dans les deux cas il n'y a pas de ventes à dire 5 mois, soit en 2007 ou en 2008.
et le fait que ce travail ne peut être fait avec une jolie requête, je voudrais certainement essayer d'obtenir les résultats que vous voulez vraiment.Aucun point en ayant une vilaine requête et pas même obtenir les données exactes que vous voulez vraiment.;)
Donc, je vous suggère de le faire en 5 étapes:
1.créer une table temporaire dans le format que vous voulez que vos résultats correspondent à
2.remplir avec douze lignes, avec 1 à 12 dans la colonne du mois de
3.mise à jour de la "Cette Année" dans la colonne à l'aide de votre SP1 logique
4.mise à jour de la "Dernière Année" de la colonne à l'aide de votre SP2 logique
5.sélectionnez à partir de la table temporaire
Bien sûr, je suppose que je suis en train de travailler à partir de l'hypothèse que vous pouvez créer une procédure stockée pour accomplir cette tâche.Vous pourriez être techniquement en mesure d'exécuter le lot entier en ligne, mais ce genre de laideur est très rarement vu.Si vous ne pouvez pas faire un SP, je vous suggère de revenir à la jointure externe complète via la sous-requête, mais il ne sera pas vous obtenir une ligne lorsqu'un mois avait pas de ventes, soit l'année.
À propos de la démarque - Oui, c'est frustrant.L'éditeur a aperçu mon tableau HTML, mais après la publication, il avait disparu - avait Donc de supprimer tout le code HTML mise en forme du post...
@kcrumley je pense que nous sommes parvenus à des conclusions similaires.Cette requête facilement s'laid.J'ai effectivement résolu ce problème avant de lire votre réponse, à l'aide d'un semblable (mais encore différentes de l'approche).J'ai accès à créer des procédures stockées et des fonctions sur la base de données de rapports.J'ai créé une fonction à valeur de Table d'accepter un produit de la catégorie et un exercice en tant que paramètre.Basé sur que la fonction va remplir un tableau contenant les 12 rangs.Les lignes seront remplis avec les données de la vue si les ventes de la disposition, si ce n'est la ligne aura valeurs à 0.
Je puis joindre les deux tables retournées par les fonctions.Car je sais que toutes les tables ont douze vagabonde, il est plus facile d'attribuer, et je peux me joindre sur la Catégorie de Produit et par Mois:
SELECT
SP1.Program,
SP1.Year,
SP1.Month,
SP1.TotalRevenue AS ThisYearRevenue,
SP2.TotalRevenue AS LastYearRevenue
FROM GetFinancialYear(@Category, 'First Look', 2008) AS SP1
RIGHT JOIN GetFinancialYear(@Category, 'First Look', 2007) AS SP2 ON
SP1.Program = SP2.Program AND
SP1.Month = SP2.Month
Je pense que votre approche est probablement un peu plus propre que le GetFinancialYear fonction est tout à fait désordre!Mais au moins cela fonctionne - ce qui me rend heureux pour l'instant ;)
L'astuce est de faire une JOINTURE avec la ISNULL pour obtenir les colonnes jointes à partir d'un tableau.J'ai l'habitude de terminer dans une vue ou une table dérivée, sinon vous devez utiliser la fonction ISNULL dans la clause where ainsi.
SELECT
Program,
Month,
ThisYearTotalRevenue,
PriorYearTotalRevenue
FROM (
SELECT
ISNULL(ThisYear.Program, PriorYear.Program) as Program,
ISNULL(ThisYear.Month, PriorYear.Month),
ISNULL(ThisYear.TotalRevenue, 0) as ThisYearTotalRevenue,
ISNULL(PriorYear.TotalRevenue, 0) as PriorYearTotalRevenue
FROM (
SELECT Program, Month, SUM(TotalRevenue) as TotalRevenue
FROM PVMonthlyStatusReport
WHERE Year = @FinancialYear
GROUP BY Program, Month
) as ThisYear
FULL OUTER JOIN (
SELECT Program, Month, SUM(TotalRevenue) as TotalRevenue
FROM PVMonthlyStatusReport
WHERE Year = (@FinancialYear - 1)
GROUP BY Program, Month
) as PriorYear ON
ThisYear.Program = PriorYear.Program
AND ThisYear.Month = PriorYear.Month
) as Revenue
WHERE
Program = 'Bikes'
ORDER BY
Month
Qui devrait vous obtenir vos exigences minimales - lignes avec un chiffre d'affaires soit en 2007 ou en 2008, ou les deux.Pour obtenir les lignes sans les ventes dans l'année, vous avez juste besoin de JOINTURE INTERNE à une 1-12 tableau de nombres (vous ne l'un de ceux, n'est-ce pas?).
J'ai peut-être tort, mais ne devriez-vous pas être à l'aide d'une jointure externe complète au lieu d'une jointure gauche?De cette façon, vous obtiendrez le "vide", les colonnes des deux tables.
À l'aide du pivot et du Sql Dynamique, nous pouvons parvenir à ce résultat
SET NOCOUNT ON
IF OBJECT_ID('TEMPDB..#TEMP') IS NOT NULL
DROP TABLE #TEMP
;With cte(Category , Revenue , Yearh , [Month])
AS
(
SELECT 'Bikes', 10000, 2008,1 UNION ALL
SELECT 'Bikes', 12000, 2008,2 UNION ALL
SELECT 'Bikes', 12000, 2008,3 UNION ALL
SELECT 'Bikes', 15000, 2008,1 UNION ALL
SELECT 'Bikes', 11000, 2007,2 UNION ALL
SELECT 'Bikes', 11500, 2007,3 UNION ALL
SELECT 'Bikes', 15400, 2007,4
)
SELECT * INTO #Temp FROM cte
Declare @Column nvarchar(max),
@Column2 nvarchar(max),
@Sql nvarchar(max)
SELECT @Column=STUFF((SELECT DISTINCT ','+ 'ISNULL('+QUOTENAME(CAST(Yearh AS VArchar(10)))+','+'''0'''+')'+ 'AS '+ QUOTENAME(CAST(Yearh AS VArchar(10)))
FROM #Temp order by 1 desc FOR XML PATH ('')),1,1,'')
SELECT @Column2=STUFF((SELECT DISTINCT ','+ QUOTENAME(CAST(Yearh AS VArchar(10)))
FROM #Temp FOR XML PATH ('')),1,1,'')
SET @Sql= N'SELECT Category,[Month],'+ @Column +'FRom #Temp
PIVOT
(MIN(Revenue) FOR yearh IN ('+@Column2+')
) AS Pvt
'
EXEC(@Sql)
Print @Sql
Résultat
Category Month 2008 2007
----------------------------------
Bikes 1 10000 0
Bikes 2 12000 11000
Bikes 3 12000 11500
Bikes 4 0 15400