Summing a field while excluding certain fields in SQL - sql

I am trying to suma column in a table, while excluding certain records that have the paid field set to true.
I am doing so like this:
SELECT SUM( cost ) AS total
FROM sales
WHERE passport = 'xxxxx'
AND paid <>1
The table is full of data, and I can display costs by themselves, or the entire total. Just adding on
AND paid <>1
Is what causes it to fail. The query does not fail as such, but NULL is returned which is quite useless.
This is the SQL for my table
CREATE TABLE sales (
id int(10) unsigned NOT NULL AUTO_INCREMENT,
uuid varchar(64) NOT NULL,
firstname varchar(64) NOT NULL DEFAULT '',
lastname varchar(64) NOT NULL DEFAULT '',
passport varchar(64) DEFAULT NULL,
product varchar(64) NOT NULL,
quantity int(11) DEFAULT NULL,
cost double DEFAULT NULL,
paymenttype varchar(64) NOT NULL DEFAULT '',
paid tinyint(1) DEFAULT NULL,
tabno varchar(64) NOT NULL,
createdby int(10) unsigned DEFAULT NULL,
creationdate datetime DEFAULT NULL,
modifiedby int(10) unsigned DEFAULT NULL,
modifieddate timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (id)
)
And the current data
INSERT INTO sales (id, uuid, firstname, lastname, passport, product, quantity, cost, paymenttype, paid, tabno, createdby, creationdate, modifiedby, modifieddate) VALUES
(20, ':8dcee958-d1ac-6791-6253-0a7344054295', 'Jason', 'Hoff', 'r454545', 'Nicaraguan nachoes', 4, 320, 'credit', 1, '23434', 2, '2010-07-06 04:10:18', 2, '2010-07-06 04:10:18'),
(19, ':3f03cda5-21bf-9d8c-5eaa-664eb2d4f5a6', 'Jason', 'Hoff', 'r454545', 'Nica Libre (doble 4 o 5 anos)', 1, 30, 'cash', NULL, '35', 2, '2010-07-06 03:35:35', 2, '2010-07-06 03:35:35'),
(18, ':f83da33b-2238-94b9-897c-debed0c3815e', 'Jason', 'Hoff', 'r454545', 'Helado con salsa de chocolate', 1, 40, 'cash', 1, '2', 2, '2010-07-05 21:30:58', 2, '2010-07-05 21:30:58');

The 'paid' value is NULL for that row. You would need to do
SELECT SUM( cost ) AS total
FROM test.sales
WHERE passport = 'r454545'
AND paid IS NULL or paid = 0
/*Or paid <> 1 as I see you are using tinyint datatype*/
Or, better would be to not allow NULLS in that column and have paid default to 0.

Your problem is that your condition does not match any rows.
The condition paid <> 1 does not match the row where paid is NULL.
Try this query: SELECT 1 <> NULL It will return NULL. A WHERE clause filters out rows in which the clause is either false or NULL.
Replace AND paid <> 1 with AND (paid IS NULL OR paid <> 1)

The book SQL Antipatterns describes this problem in detail, section Searching Nullable Columns. Strongly recommended book.

SELECT SUM( cost ) AS total
FROM sales
WHERE passport = 'xxxxx'
AND paid = false or paid is null
EDITED
use
IS NOT
instead of
!=
This should work
SELECT SUM( cost ) AS total
FROM sales
WHERE passport = 'xxxxx'
AND paid IS NOT true

This may mean there are no rows in the table for which paid <> 1. Or maybe there are but they cost of NULL.

I would not use "<>", but use "!=" instead;
SELECT SUM( cost ) AS total
FROM sales
WHERE passport = 'xxxxx'
AND paid != 1
If that doesn't work, can you post the table definition? And are there any records which do not have paid = 1?

Related

Insert only unique records in SQL

I ran the below query in SQL to insert records (this is just a snippet)
insert into list_member (list_id, list_int_value , list_float_value , list_decimal_value , list_varchar_value, list_datetime_value, modified_by, asof_time, do_not_audit)
select 42, null, null, security_id, null, null, 10, null, null
from security
where not exists(select user_id_4 from list_member.user_id_4 where list_member.user_id_4 = security.user_id_4)
and deleted = 0 and user_id_4 in
(
'ES0125220311',
'ES0132105018',
'ES0167050915'
)
Now I have another list to insert, but only want to insert new records.
I'm unsure where to insert the additional 'where' clause so that it doesn't insert duplicates. I've come up with the below (which in theory should only add the final record), but the additional where clause in bold is likely wrong ...
insert into list_member (list_id, list_int_value , list_float_value , list_decimal_value , list_varchar_value, list_datetime_value, modified_by, asof_time, do_not_audit)
select 42, null, null, security_id, null, null, 10, null, null
from security
**where not exists(select user_id_4 from list_member.user_id_4 where list_member.user_id_4 = security.user_id_4)**
and deleted = 0 and user_id_4 in
(
'ES0125220311',
'ES0132105018',
'ES0167050915',
'ES0123456789'
)
Anyone able to assist?
I changed it to this instead
left join list_member
on list_member.list_id = 42 and list_member.list_decimal_value = security.security_id
Sorry for the trouble.
You can use corelated query as follows:
insert into list_member (list_id, list_int_value , list_float_value , list_decimal_value , list_varchar_value, list_datetime_value, modified_by, asof_time, do_not_audit)
select 42, null, null, security_id, null, null, 10, null, null
from security s
where not exists
(select 1 from list_member l where l.user_id_4 = s.user_id_4)
and deleted = 0 and user_id_4 in
(
'ES0125220311',
'ES0132105018',
'ES0167050915',
'ES0123456789'
)

Cumulative query with 'group by' causing slowness

We have a table which will store the information of Users like deals performed, date etc. Same user can perform same deals multiple times. Now we want to display the cumulative sum of amount user performed totally if deal no is same
Query with GROUP BY Causing Slowness
Query to display cumulative sum joining the same table.
Table has 2.7 million records.
And this goes for proper index scan (Index structure).
INDEX `IDX_GRPBY2` (`DEAL`, `FIN`),
INDEX `Table1_TEMP` (`CREATE1`, `FEVSTATUS`, `EVE`)
Below is the expected result
Expected Result
Below is the actual query which is used to get deals with cumulative sum if it's same deal:
SELECT MAX(A.PKEY) PKEY,
A.ENT, A.DEAL, A.FIN, A.DATE1, A.TYPE1, A.STEPNO,
A.CREATE1, A.EVE, A.FEVSTATUS, A.STATUSDATE, A.INTAMT,
A.INTPAID, A.INT_TYPE, A.PENALTY_1, A.CONV_PAID_4,
A.INT_PAID_4, A.CONVFIN_4, A.INTTYPE,
B.DEAL AS DEAL_B, B.FIN AS FIN_B,
SUM(B.CONV_PAID_4) AS CONV_PAID_4_OUT
FROM Table1 A, Table1 B
WHERE A.CREATE1 = '0'
AND (A.FEVSTATUS = '1'
OR A.EVE IN ('E06', 'E07', 'E02', 'E15', 'E03', 'E04')
)
AND A.DEAL = B.DEAL
AND A.FIN = B.FIN
AND A.PKEY >= B.PKEY
GROUP BY A.ENT, A.DEAL, A.FIN, A.DATE1, A.TYPE1, A.STEPNO,
A.CREATE1, A.EVE, A.FEVSTATUS, A.STATUSDATE, A.INTAMT,
A.INTPAID, A.INT_TYPE, A.PENALTY_1, A.CONV_PAID_4,
A.INT_PAID_4, A.CONVFIN_4, A.INTTYPE,
B.DEAL, B.FIN;
I tried to change to below to sum up in sub query and then join, but still the result is taking very long
Changed Query:
SELECT MAX(A.PKEY) PKEY, A.ENT, A.DEAL, A.FIN, A.DATE1, A.TYPE1,
A.STEPNO, A.CREATE1, A.EVE, A.FEVSTATUS, A.STATUSDATE,
A.INTAMT, A.INTPAID, A.INT_TYPE, A.PENALTY_1, A.CONV_PAID_4,
A.INT_PAID_4, A.CONVFIN_4, A.INTTYPE, A.DEAL AS DEAL_B,
A.FIN AS FIN_B,
(
SELECT SUM(B.CONV_PAID_4)
FROM Table1 B
WHERE A.DEAL = B.DEAL
AND A.FIN = B.FIN
AND A.PKEY >= B.PKEY
) AS PRI_CONV_PAID_4_OUT
FROM Table1 A
WHERE A.CREATE1 = '0'
AND (A.FEVSTATUS = '1'
OR A.EVE IN ('E06', 'E07', 'E02', 'E15', 'E03', 'E04')
)
GROUP BY A.ENT, A.DEAL, A.FIN, A.DATE1, A.TYPE1, A.STEPNO,
A.CREATE1, A.EVE, A.FEVSTATUS, A.STATUSDATE, A.INTAMT,
A.INTPAID, A.INT_TYPE, A.PENALTY_1, A.CONV_PAID_4, A.INT_PAID_4,
A.CONVFIN_4, A.INTTYPE, B.DEAL, B.FIN;
Any help in re-framing the query faster?
Below the show create table
CREATE TABLE `Table1` (
`PKEY` DECIMAL(10,0) NOT NULL DEFAULT '0',
`ENT` CHAR(3) NOT NULL DEFAULT '',
`DEAL` CHAR(14) NOT NULL DEFAULT '',
`FIN` CHAR(3) NOT NULL DEFAULT '',
`DATE1` DATETIME NULL DEFAULT NULL,
`TYPE1` CHAR(3) NULL DEFAULT NULL,
`STEPNO` CHAR(3) NULL DEFAULT NULL,
`CREATE1` CHAR(1) NULL DEFAULT NULL,
`EVE` CHAR(3) NULL DEFAULT NULL,
`FEVSTATUS` CHAR(1) NULL DEFAULT NULL,
`STATUSDATE` DATETIME NULL DEFAULT NULL,
`INTAMT` DECIMAL(19,5) NULL DEFAULT NULL,
`INTPAID` DECIMAL(19,5) NULL DEFAULT NULL,
`INT_TYPE` CHAR(1) NULL DEFAULT NULL,
`PENALTY_1` DECIMAL(9,6) NULL DEFAULT NULL,
`CONV_PAID_4` DECIMAL(15,2) NULL DEFAULT NULL,
`CONV_PAID_4` DECIMAL(19,5) NULL DEFAULT NULL,
`CONV_FIN_4` CHAR(3) NULL DEFAULT NULL,
`INTTYPE` CHAR(1) NULL DEFAULT NULL,
PRIMARY KEY (`PKEY`),
UNIQUE INDEX `IXQDWFEV_PK` (`PKEY`),
UNIQUE INDEX `IXIDWFEV` (`ENT`, `DEAL`, `FIN`, `DATE1`),
INDEX `IXQDWFEV_GI1` (`ENT`, `DEAL`, `CONV_FIN_4`),
INDEX `IXQDWFEV_GI2` (`ENT`, `DEAL`, `TYPE1`, `STEPNO`),
INDEX `IDX_GRPBY2` (`DEAL`, `FIN`),
INDEX `IXQDWFEV1_TEMP` (`CREATE1`, `FEVSTATUS`, `EVE`)
)
COLLATE='utf8_general_ci'
;

SQL - Split values from one column into two separate columns

I am trying to take the value of column Quantity and split the values where Quantity is less than 500 and put those records into the LessThan500 column, for the records that are greater than 500 into the GreaterThan500 column.
I have a select statement that does this, but the table does not get updated properly
SUM(CASE WHEN Quantity < 500 THEN CAST(( Quantity*10 ) AS INT)
ELSE ''
END) as Quantityx10
, SUM(CASE WHEN Quantity < 500 THEN CAST(( Quantity*10 ) AS INT)
ELSE ''
END) AS '<500'
, '' AS '>=500'
USE FET
-- Create a new template table
IF OBJECT_ID('dbo.FuelSales', 'U') IS NOT NULL
DROP TABLE dbo.FuelSales;
CREATE TABLE FuelSales
(
TransactionType varchar(1) null,
TransactionID int,
CustomerID int null,
TransactionDate date null,
EntryTimeofDay datetime null,
UserTranNumber varchar(15) null,
AircraftNumber varchar(10) null,
CompanyName varchar(40) null,
NumNameCode varchar(30) null,
Description varchar(40) null,
Quantity decimal(18,6) null,
LessThan500 decimal(18, 6) null,
GreaterThan500 decimal(18,6) null,
);
INSERT INTO FuelSales(TransactionType, TransactionID, CustomerID, TransactionDate, EntryTimeofDay,UserTranNumber,AircraftNumber,CompanyName,NumNameCode,Description,Quantity,LessThan500,GreaterThan500)
SELECT ci.TransactionType
, ci.TransactionID
, ci.CustomerID
, ci.TransactionDate
, ci.EntryTimeofDay
, ci.UserTranNumber
, d.AircraftNumber
, c.CompanyName
, d.NumNameCode
, d.Description
, Quantity
FROM [TFBO7].[dbo].[CustInv] ci
INNER JOIN [TFBO7].[dbo].[Cust] c
ON c.CustomerID=ci.CustomerID
INNER JOIN [TFBO7].[dbo].[CustIDet] d
ON ci.TransactionID=d.TransactionID
WHERE ci.TransactionDate between '20180701' and '20180731' and d.TransactionTypeID='1'
I'm not sure why would you do this permanently on the table and not just do the separation as an ad hoc query when needed but assuming it's SQL Server it would go something like this (in the INSERT statement you're already doing):
...
, Quantity
-- 500 has to go somewhere so I put it in the first category
, LessThan500 = CASE WHEN Quantity <= 500 THEN Quantity ELSE NULL END
, GreaterThan500 = CASE WHEN Quantity > 500 THEN Quantity ELSE NULL END
FROM [TFBO7].[dbo].[CustInv] ci
...
You can add a computed column to the table. Just do:
alter table fuelsales add lessthan500 as (case when quantity <= 500 then quantity end);
alter table fuelsales add greaterthan500 as (case when quantity > 500 then quantity end);
This column will appear in the table when you query the table. However, it is not stored and it is "updated" automatically when quantity changes.

How can I calculate the duration a contact has been in any of the availability states in this database?

Given the following tables:
CREATE TABLE [Contact]
(
[Id] INTEGER NOT NULL,
[Uri] CHARACTER VARYING(255) NOT NULL,
[CreatedOn] DATETIMEOFFSET NOT NULL
);
CREATE TABLE [Availability]
(
[Id] TINYINT NOT NULL,
[Name] CHARACTER VARYING(255) NOT NULL,
[CreatedOn] DATETIMEOFFSET NOT NULL
);
CREATE TABLE [ContactAvailability]
(
[Id] BIGINT NOT NULL,
[ContactId] INTEGER NOT NULL,
[AvailabilityId] INTEGER NOT NULL,
[CreatedOn] DATETIMEOFFSET NOT NULL
);
I am attempting to get a list of all of the contacts and the durations for which they have been in any of the availabilities for the current day.
The ContactAvailability table ends up having records such as:
(1, 1, 1, '01/01/2014 08:00:23.51 -07:00'),
(2, 1, 3, '01/01/2014 08:15:38.01 -07:00'),
(3, 1, 3, '01/01/2014 08:15:38.02 -07:00'),
(4, 2, 2, '01/01/2014 08:18:33.12 -07:00')
These records represent a Contact's transition from one Availability to another, and also from one Availability to the same. It is essentially a running status that is logged on an interval.
The query I have come up with only queries for a particular user and only gets a list of their availabilities for the current day, but it won't calculate how long the Contact has been in any Availability. I am not sure where to start when it comes to that.
This is that query:
SELECT [Contact].[Uri] AS [ContactUri],
[Availability].[Name] AS [AvailabilityName],
[ContactAvailability].[CreatedOn]
FROM [ContactAvailability]
INNER JOIN [Contact] ON [Contact].[Id] = [ContactAvailability].[ContactId]
INNER JOIN [Availability] ON [Availability].[Id] = [ContactAvailability].[AvailabilityId]
WHERE [Contact].[Uri] = 'sip:contact#example.com' AND
[ContactAvailability].[CreatedOn] >= '06/30/2014 00:00:00 -07:00' AND
[ContactAvailability].[CreatedOn] < '07/01/2014 00:00:00 -07:00'
You can use a Window Function in combination with a CTE.
I think this should work, not tested yet :) So you might have to change your column names.
with SourceTable
( ContactID, AvailabilityID, NewDate, OldDate)
as(
SELECT ContactAvailability.ContactID AS ContactID,
ContactAvailability.AvailabilityID AS AvailabilityID,
[ContactAvailability].[CreatedOn] As NewDate,
LAG(ContactAvailability.CreatedON) OVER (Partition By ContactAvailability.ContactID order by ContactAvailability.CreatedOn) as OldDate
FROM [ContactAvailability])
SELECT [Contact].[Uri] AS [ContactUri],
[Availability].[Name] AS [AvailabilityName],
SourceTable.OldDate as PreviousAvailabilityDate,
SourceTable.NewDate as CurrentAvailibilityDate,
SourceTable.NewDate - SourceTable.OldDate as DifferenceBetweenAvailability,
[ContactAvailability].[CreatedOn]
FROM SourceTable
INNER JOIN [Contact] ON [Contact].[Id] = SourceTable.[ContactId]
INNER JOIN [Availability] ON [Availability].[Id] = SourceTable.[AvailabilityId]
If you need to calculate the total time somebody has been in a certain availability (f.e. personA is in availability A then B then A again and then C) you will have to add another cte and partition on ContactAvailability.AvailabilityID and then make a sum of your calculated field.

SQL and Access calculating a sum value based on a variable field.

Applogies if you saw my previous post. It was 'closed' for being open ended.
What i have is a three table system for Cars Salesman and Customers. This is mimicking a very simple Car sales system.
Within the Car Table is the following fields:
Registation:
Make:
Model:
Date of Purchase:
Customer ID:
Salesman ID:
Date Sold:
Date bought:
Price:
or each sale, made by a salesperson, they will earn a commission based on the following table:
Car Price(£) % Commission
0-5000 5
5000-10000 6
10000-15000 7
15000+ 8
The car is always sold for the price specified in the car details table.
Do I commission rate somewhere within the table (and automatically calculate this)r as a separate table.
If I am going to store this within the Car Table, how would I get the database to automatically calculate the commision and put it in the table, so that when the car is sold ?
Thanks
the simplest solution, programmatically speaking, would be to create a table COMMISSION with 4 fields: Id_Commission, Nr_Commission_Min_Value, Nr_Commission_Max_Value, Nr_Commission_Percent
you would then call a select statement like (tested and working in mysql, whose tsql is very, very similar to msaccess's):
SELECT DISTINCT Id_Car, Car.Ds_Car_Description, Car.Nr_Car_Price, Commission.Nr_Commission_Percent * Car.Nr_Car_Price / 100 As Commission_Payable FROM Car LEFT JOIN Commission ON (Car.Nr_Car_Price BETWEEN Commission.Nr_Commission_Min_Value AND Commission.Nr_Commission_Max_Value) WHERE Car.Ic_Car_Sold = 1 ORDER BY Car.Nr_Car_Price DESC
below, the sql for creating and populating these tables.
CREATE TABLE IF NOT EXISTS Commission (
Id_Commision int(11) NOT NULL,
Nr_Commission_Min_Value double NOT NULL,
Nr_Commission_Max_Value double NOT NULL,
Nr_Commission_Percent double NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin;
INSERT INTO Commission (Id_Commision, Nr_Commission_Min_Value, Nr_Commission_Max_Value, Nr_Commission_Percent) VALUES
(0, 0, 5000, 5),
(1, 5001, 10000, 6),
(2, 10001, 15000, 7),
(3, 15001, 1000000000000, 8);
CREATE TABLE IF NOT EXISTS Car (
Id_Car int(11) NOT NULL AUTO_INCREMENT,
Ds_Car_Description varchar(20) COLLATE utf8_bin NOT NULL,
Nr_Car_Price double NOT NULL,
Ic_Car_Sold tinyint(1) NOT NULL DEFAULT '0',
PRIMARY KEY (Id_Car)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin AUTO_INCREMENT=7 ;
INSERT INTO Car (Id_Car, Ds_Car_Description, Nr_Car_Price, Ic_Car_Sold) VALUES
(1, 'Lotus', 20000, 1),
(2, 'Renault 5', 1000, 1),
(3, 'Audi A3', 6000, 1),
(4, 'Fiat Mille', 300, 1),
(5, 'Land Rover', 12000, 0),
(6, 'Lotus', 25000, 1);
in naming fields, i use Id for main index, Nr for numbers, Ds for strings and Ic for booleans. a standard i picked up designing dbs for Banco do Brasil.